Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

A spoonerisation is swapping the first letter/pronounceable syllable of two or more words.

Example: A lack of pies = A pack of lies. Bad salad = Sad ballad (ish) :)

Challenge: Write a program to spoonerise two words in the shortest characters possible.

Rules:

  1. If the first letter is a consonant, you must include subsequent letters until you reach a vowel. (e.g. 'thanks' - the th gets removed and put on the front of the other word).
  2. With words that start with vowels, do not remove anything from the start of the word.

Good luck :)


UPDATE: Multiple word spoonerisms are not strictly applicable, however from a programatic view we must cater for them so this is the rule: Every word must change. Example:

Weird Keen Monkey -> Meird Ween Konkey OR Keird Meen Wonkey NOT Meird Keen Wonkey

Also 'y' is treated as a vowel (just to simplify things a bit)

share|improve this question
    
To clarify, does "big apple" become "ig bapple", and does "ant eater" remain the same? –  Joey Adams Aug 16 '11 at 22:26
    
indeed - unless you can think of a better rule in which case I will add it in there :) –  Alex Coplan Aug 16 '11 at 22:27
    
A "better rule" might involve using a word list to find an "optimal" spoonerization, but that would be a different and more complex problem. –  Joey Adams Aug 16 '11 at 22:32
1  
I'm sorry but I have to downvote the question, which wasn't well prepared. So the answers were coming in, while the question wasn't cleared. Related discussion: more discipline Place to prepare challenges and to find and remove ambiguities, beside chat: Sandbox mk II –  user unknown Aug 20 '11 at 16:21
2  
The original question says "spoonerise two words". That seemed clear, and downvoting answers (including mine) that followed that spec doesn't sit right with me. The updated question says "every word must change". How should "a" in "a pack of lies" or "I" in "I like my bike" change? Presumably, the intent is that every word that starts with a consonant should change? Sorry, but I think the original was clearer, even though the examples of spoonerisms weren't examples of input. –  DCharness Aug 21 '11 at 6:35
show 16 more comments

8 Answers

Ruby (60) (61) (93) (80) (75) (71) (69 / 57)

Program changed to handle the new requirement of handling multiple words.

EDIT: Golfed down to 80. 75. 71. 69.

w=[];gets.gsub(/(\S*?)([aeiouy]\S*)/i){w+=[$2,' ',$1]};$><<w.pop+w*''

If we're only spoonerizing words that start with a consonant, here's a solution in 73. 65. (I realized I can just use scan instead of gsub.) 57.

$><<gets.gsub(p=/\b[^aeiouy\s]+/i){($'+$`+$&).scan(p)[0]}

Old program (two words only):

p='(.*?)([aeiou].+)';puts gets.gsub(/#{p} #{p}/i,'\3\2 \1\4')
share|improve this answer
    
Sorry - didn't see your answer until I posted mine. –  Lowjacker Aug 17 '11 at 0:01
    
No problem. I figure it's the simplest approach with Ruby anyway. And I learned a couple more tricks from seeing yours ;) –  migimaru Aug 17 '11 at 0:03
    
echo "I like brasilian football" | irb spo-2.rb p='(.*?)([aeiou].+)';puts gets.gsub(/#{p} #{p}/i,'\3\2 \1\4') fI like brasilian ootball –  user unknown Aug 20 '11 at 6:15
3  
The original problem only asked for spoonerizing two words. I don't know when the spec changed, but I'll fix this later. –  migimaru Aug 20 '11 at 8:23
    
From the question log A lack of pies was part of the initial question - 2 words (lack, pies) is the number of words starting with a consonant, isn't it? –  user unknown Aug 20 '11 at 15:51
show 3 more comments

Haskell, 81

f[(a,b),(c,d)]=unwords[c++b,a++d]
main=interact$f.map(break(`elem`"aeiou")).words
share|improve this answer
    
May I ask you, after seeing 3 solutions failing miserably for untrivial input, how you handle I like brazilian football? (3 words, starting with consonants, and one word, just being one character? What about 'Why do you cheat'? :) –  user unknown Aug 20 '11 at 6:27
    
y is treated as a vowel therefore dy who chou yeat - off the top of my head, but when I wrote the question I was only aiming for 2 words –  Alex Coplan Aug 20 '11 at 7:36
add comment

Ruby, 57 56

This only works for the original problem (with only two words).

$><<gets.sub(/#{r='([^aeiou]*)(.+)'} #{r}/i,'\3\2 \1\4')
share|improve this answer
    
Assuming the input is always valid, you can replace \w+ with .+ to save another character. –  migimaru Aug 17 '11 at 0:55
    
echo "consonants are really evil" | irb spo-1.rb $><<gets.sub(/#{r='([^aeiou]*)(.+)'} #{r}/i,'\3\2 \1\4') onsonants are really cevil –  user unknown Aug 20 '11 at 6:13
add comment

J, 77

Answers the original version of the question, "spoonerise two words"; a solution for the changed question may follow.

Couldn't shrink this to compete with the Ruby answers, so fell back on my more-familiar Perl.

exit([:;3 1 2 0 4{[:,({.@I.@e.&'aeiouAEIOU'({.;' ';~}.)]);._2@,&' ')&.stdin''
  • ,&' ' appends a space

  • (...);._2 splits on the last character (the space) and applies the parenthesized code to each element (i.e., each word)

  • {. @ I. @ e.&'aeiouAEIOU' finds the index of the first vowel in that word

  • ({.;' ';~}.) splits the word into leading consonants (if any) and a tail, with a space at the end (each in a "box", so J doesn't pad them to matching lengths)

  • , flattens the 2r x 3c matrix formed by the preceding into a 6-element list

  • 3 1 2 0 4{ takes all those elements but the trailing space, swapping the lead segments of the words

  • ; unboxes and concatenates the elements

  • (...)&.stdin'' does roughly the same as Perl's -p, reading stdin and echoing the result of "..." to stdout

  • the explicit exit suppresses J's prompt for more input

share|improve this answer
add comment

Perl, 43 (question v1), 91 79 (question v2) [107 to handle numerals in the phrase]

v1

Run with -p option (counted as 3 chars).

$p='([^aeiou]*)(.+)';s/$p $p/$3$2 $1$4/i

Same technique as the Ruby answers.

Using -p saves 9 characters over bracketing the code with $_=<>; and ;print for explicit IO.

v2

Run with -lp (counted as 4 chars).

push@a,$1while s/\b([^ aeiouy\d]+)(?=[aeiouy])/$n++/ei;unshift@a,pop@a;s/(\d+)/$a[$1]/g

Rotates all the consonant-starts of words one word forward.

Updated: Add vowel lookahead, so it handles "to the nth degree" more gracefully ("do te nth thegree", not "do te th nthegree").

Numerals: user unknown asked about handling "There is no winner in 2011", because of the numerals. Adding markers around the numbers my solution uses, it can handle that:

push@a,$1while s/\b([^ aeiouy\0\d]+)(?=[aeiouy])/"\0".$n++."\0"/ei;unshift@a,pop@a;s/\0(\d+)\0/$a[$1]/g

Small question, big discussion....

share|improve this answer
    
echo "I like my bike" | perl5.10.1 -p spoon.pl bI like my ike –  user unknown Aug 20 '11 at 6:19
    
I'm of the opinion that numerals and doing special case handling for words like 'nth' are beyond the spec. We're already treating 'y' as a vowel in all cases (even in words like 'you'), so I don't see why you're starting to take pronunciation into account. –  migimaru Aug 22 '11 at 15:38
    
@migimaru: With 'nth', pronunciation wasn't my concern, but motion of the entire word. Hard to tell, the way this question has progressed, what a solution should handle or not. Anyhow, nicely done with the ruby solution. –  DCharness Aug 22 '11 at 15:54
add comment

CSharp - 236

List<string> f(string w) {var t="aeiou".ToCharArray();var q=w.Split(' ').Where(c=>!t.Contains(c[0]));var i=q.Count()-1;return q.Select(c=>q.Select(x=>x.Substring(0,x.IndexOfAny(t))).ToList()[i--]+c.Substring(c.IndexOfAny(t))).ToList();}
share|improve this answer
    
Is this a program or a function? –  user unknown Aug 20 '11 at 14:27
add comment

Scala 244

val s=readLine
def f(a:String,b:String)=a.replaceAll("^[^aeiouy]+",b.replaceAll("[aeiouy].*",""))
val l=("((^| )[^aeiouyAEIOUY]+)([^ ]+)".r findAllIn s map(_.trim)).toList
println(s.replaceAll(l(0),f(l(0),l(1))).replaceAll(l(1),f(l(1),l(0))))

My code contains 4 y (one of them lowercase) to handle why and rythm in a useful manner.


Scala 248, updated for new challenge (change all consonantic words)

val w=readLine.split(" ")
val c=w.filter(_.matches("[^aeiouyAEIOUY].*"))
val l=c.size
var k=0
println((for(i<-0 to w.size-1)yield{if(w(i)==c(k%l)){k+=1
c(k-1).replaceAll("^[^aeiouy]+",c(k%l).replaceAll("[aeiouy].*",""))}else w(i)}).mkString(" "))

ungolfed, and test cases:

val samples = List ("bad salad", "I tell smooth", "Sentence with vowel in the end", "tain bruck is faboo")
def konsInit (s: String) = s.matches ("[^aeiouyAEIOUY].*")
def flipInit (a: String, b: String) = a.replaceAll ("^[^aeiouy]+", b.replaceAll ("[aeiouy].*", "")) 
def spoonize (sentence: String) {
  val words = sentence.split (" ")
  val kwords = words.filter (konsInit);
  val len = kwords.size
  var k = 0
  val spoonies = for (i <- 0 to words.size-1) yield {
    if (words (i) == kwords (k%len)) {
      k += 1
      flipInit (kwords (k-1), kwords (k%len)) 
    } else words (i) 
  }
  println (spoonies.mkString (" ")) 
}
samples.foreach (spoonize)

sad balad
I smell tooth
wentence vith thowel in Se end

The last example can't be shown.

share|improve this answer
    
I'm not sure what we're considering correct behavior for all-consonant words, but "to the nth degree" => "tho nthe d tegree" seems sub-optimal. –  DCharness Aug 21 '11 at 16:57
    
Is 'nth' considered a regular word? Abbrevations without vowels, numbers, dates or sentence marks have not been discussed too - do you handle 'There is no winner in 2011'? My dictionary contains no word between nozzle and nuance. But meanwhile I recognized that all others interpret the question different than me, and try to solve the big apple problem. I guess I will delete my post. –  user unknown Aug 21 '11 at 17:13
    
Foremost, I do not suggest you delete your post. You do seem to have been rather critical (as in offering critique, not necessarily negative) of others', so I thought you'd want the feedback. Nth is indeed a word (e.g., see merriam-webster.com/dictionary/nth). My code does not handle digits, though it should be adaptable. –  DCharness Aug 21 '11 at 18:10
    
Yes, 'nth' is a special case, worth mentioning, but until today I would have said, that there isn't a word without vowel in the English language. My dictionary is 'Langenscheidt', by the way. I could try to rewrite my solution, but I'm already outside of the specs with acting wrong on 'big apple'. Now I slowly get enough of this. You can always find special cases, but this puzzle was nearly not prepared. From the question, I can't derive how my program should act on input like 'nth'. –  user unknown Aug 21 '11 at 21:34
    
nth is pronounced 'enth', so you could treat it like it starts with a vowel. –  Gareth Aug 22 '11 at 9:31
add comment

PHP , 175,147

gets and change $s

foreach(split(' ',$s)as$v){preg_match('~^([^aieouy]*)(.*)~i',$v,$m[(int)@$n++]);}foreach($m as$x=>$r){$a[]=$m[($x+1)%$n][1].$r[2];}$s=join(' ',$a);
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.