9
\$\begingroup\$

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)

\$\endgroup\$
21
  • \$\begingroup\$ To clarify, does "big apple" become "ig bapple", and does "ant eater" remain the same? \$\endgroup\$
    – Joey Adams
    Aug 16, 2011 at 22:26
  • \$\begingroup\$ indeed - unless you can think of a better rule in which case I will add it in there :) \$\endgroup\$ Aug 16, 2011 at 22:27
  • \$\begingroup\$ A "better rule" might involve using a word list to find an "optimal" spoonerization, but that would be a different and more complex problem. \$\endgroup\$
    – Joey Adams
    Aug 16, 2011 at 22:32
  • 1
    \$\begingroup\$ 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 \$\endgroup\$ Aug 20, 2011 at 16:21
  • 2
    \$\begingroup\$ 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. \$\endgroup\$
    – DCharness
    Aug 21, 2011 at 6:35

7 Answers 7

3
\$\begingroup\$

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')
\$\endgroup\$
8
  • \$\begingroup\$ Sorry - didn't see your answer until I posted mine. \$\endgroup\$
    – Lowjacker
    Aug 17, 2011 at 0:01
  • \$\begingroup\$ No problem. I figure it's the simplest approach with Ruby anyway. And I learned a couple more tricks from seeing yours ;) \$\endgroup\$
    – migimaru
    Aug 17, 2011 at 0:03
  • \$\begingroup\$ 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 \$\endgroup\$ Aug 20, 2011 at 6:15
  • 3
    \$\begingroup\$ The original problem only asked for spoonerizing two words. I don't know when the spec changed, but I'll fix this later. \$\endgroup\$
    – migimaru
    Aug 20, 2011 at 8:23
  • \$\begingroup\$ 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? \$\endgroup\$ Aug 20, 2011 at 15:51
1
\$\begingroup\$

Haskell, 81

f[(a,b),(c,d)]=unwords[c++b,a++d]
main=interact$f.map(break(`elem`"aeiou")).words
\$\endgroup\$
2
  • \$\begingroup\$ 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'? :) \$\endgroup\$ Aug 20, 2011 at 6:27
  • \$\begingroup\$ 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 \$\endgroup\$ Aug 20, 2011 at 7:36
1
\$\begingroup\$

Ruby, 57 56

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

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

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

\$\endgroup\$
1
\$\begingroup\$

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....

\$\endgroup\$
3
  • \$\begingroup\$ echo "I like my bike" | perl5.10.1 -p spoon.pl bI like my ike \$\endgroup\$ Aug 20, 2011 at 6:19
  • \$\begingroup\$ 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. \$\endgroup\$
    – migimaru
    Aug 22, 2011 at 15:38
  • \$\begingroup\$ @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. \$\endgroup\$
    – DCharness
    Aug 22, 2011 at 15:54
0
\$\begingroup\$

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();}
\$\endgroup\$
1
  • \$\begingroup\$ Is this a program or a function? \$\endgroup\$ Aug 20, 2011 at 14:27
0
\$\begingroup\$

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.

\$\endgroup\$
5
  • \$\begingroup\$ 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. \$\endgroup\$
    – DCharness
    Aug 21, 2011 at 16:57
  • \$\begingroup\$ 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. \$\endgroup\$ Aug 21, 2011 at 17:13
  • \$\begingroup\$ 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. \$\endgroup\$
    – DCharness
    Aug 21, 2011 at 18:10
  • \$\begingroup\$ 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'. \$\endgroup\$ Aug 21, 2011 at 21:34
  • \$\begingroup\$ nth is pronounced 'enth', so you could treat it like it starts with a vowel. \$\endgroup\$
    – Gareth
    Aug 22, 2011 at 9:31

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