Program to Spoonerise words

9

1

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)

Alex Coplan

Posted 2011-08-16T22:05:53.027

Reputation: 209

Question was closed 2016-01-14T03:22:43.177

To clarify, does "big apple" become "ig bapple", and does "ant eater" remain the same? – Joey Adams – 2011-08-16T22:26:54.397

indeed - unless you can think of a better rule in which case I will add it in there :) – Alex Coplan – 2011-08-16T22:27:32.337

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 – 2011-08-16T22:32:12.220

Indeed, and would not be so easily golfable as it relies on the quality of your source model / API – Alex Coplan – 2011-08-16T22:34:29.343

I can't think of a retter bule. – Briguy37 – 2011-08-17T18:39:51.727

Does the input have to be on STDIN? – recursive – 2011-08-17T19:08:09.627

@recursive - sorry what is STDIN? I'm an obj-c programmer – Alex Coplan – 2011-08-17T19:44:46.893

Standard input a.k.a. console input. – recursive – 2011-08-17T19:48:05.967

in that case, yes – Alex Coplan – 2011-08-17T19:58:41.277

You don't have to verify that the word exists in a dictionary - just swap? So program to would become togram pro ..., spoonerise words => woonerise spords? – user unknown – 2011-08-18T15:46:37.853

that's correct :) – Alex Coplan – 2011-08-18T16:52:39.257

it was purely a coding challenge, not that it has a practical application! – Alex Coplan – 2011-08-18T16:53:02.623

What shall 'no year solution' be transformed to? 'yo near solution'? Or "Why do you cheat" or "rhythm and soul". – user unknown – 2011-08-20T05:02:46.827

@user unkown see updated answer – Alex Coplan – 2011-08-20T07:44:01.773

updated question? – user unknown – 2011-08-20T12:58:28.117

yes sorry I'm so used to typing see updated answer :) – Alex Coplan – 2011-08-20T13:06:14.063

a pack of lies was 4 words too, just only 2 starting with a consonant. So I thought, according with the old rules, we have to change the first 2 words, which don't start with a vowel. Now you changed the rules completly. Why? That's not clarification of the rules, but changing them. – user unknown – 2011-08-20T13:06:26.543

3 of the current solutions fail with 'a pack of lies' and return 'la pack of ies' - I didn't test that, because I wouldn't have thought somebody is presenting a solution, which does not solve the given examples. – user unknown – 2011-08-20T14:22:27.417

To be tested with "Stack Overflow Rocks Hard"... – PhiLho – 2011-08-20T16:01:52.823

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 – 2011-08-20T16:21:09.160

2The 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 – 2011-08-21T06:35:11.313

Answers

3

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')

migimaru

Posted 2011-08-16T22:05:53.027

Reputation: 1 040

Sorry - didn't see your answer until I posted mine. – Lowjacker – 2011-08-17T00:01:33.650

No problem. I figure it's the simplest approach with Ruby anyway. And I learned a couple more tricks from seeing yours ;) – migimaru – 2011-08-17T00:03:52.397

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 – 2011-08-20T06:15:33.493

3The original problem only asked for spoonerizing two words. I don't know when the spec changed, but I'll fix this later. – migimaru – 2011-08-20T08:23:17.153

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 – 2011-08-20T15:51:48.600

I think you're interpreting the post wrong. Those are examples of spoonerisms, but the "Challenge" is "Write a program to spoonerise two words in the shortest characters possible." – migimaru – 2011-08-20T15:56:57.710

Also, "2 words" does not refer just to the number of words starting with a consonant (which is consistent with the actual definition of a spoonerism). See Alex's comment on the question where he confirms that "big apple" becomes "ig bapple." – migimaru – 2011-08-20T18:46:54.240

A lack of pies = A pack of lies was a solution in accordance with the rules: skip words which start with vowels. But now I see, how all that makes sense too - you ignored the example with 4 words, because it wasn't two words, and I translated '2 words' to '2 words, starting with consonants'. Now the question is still ambiguous: Alex says 'every word must change' and presents 3 words with different consonants. so we will survive can be modified, according to the rules, but what to do with we will survive? I'm sorry for all that trouble, but don't feel guilty. – user unknown – 2011-08-21T05:54:26.977

1

Haskell, 81

f[(a,b),(c,d)]=unwords[c++b,a++d]
main=interact$f.map(break(`elem`"aeiou")).words

Joey Adams

Posted 2011-08-16T22:05:53.027

Reputation: 9 929

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 – 2011-08-20T06:27:22.617

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 – 2011-08-20T07:36:11.867

1

Ruby, 57 56

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

$><<gets.sub(/#{r='([^aeiou]*)(.+)'} #{r}/i,'\3\2 \1\4')

Lowjacker

Posted 2011-08-16T22:05:53.027

Reputation: 4 466

Assuming the input is always valid, you can replace \w+ with .+ to save another character. – migimaru – 2011-08-17T00:55:13.663

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 – 2011-08-20T06:13:03.937

1

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

DCharness

Posted 2011-08-16T22:05:53.027

Reputation: 541

echo "I like my bike" | perl5.10.1 -p spoon.pl bI like my ike – user unknown – 2011-08-20T06:19:43.370

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 – 2011-08-22T15:38:42.227

@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 – 2011-08-22T15:54:05.970

1

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

DCharness

Posted 2011-08-16T22:05:53.027

Reputation: 541

0

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();}

Rob

Posted 2011-08-16T22:05:53.027

Reputation: 167

Is this a program or a function? – user unknown – 2011-08-20T14:27:31.137

0

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.

user unknown

Posted 2011-08-16T22:05:53.027

Reputation: 4 210

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 – 2011-08-21T16:57:41.307

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 – 2011-08-21T17:13:50.487

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 http://www.merriam-webster.com/dictionary/nth). My code does not handle digits, though it should be adaptable.

– DCharness – 2011-08-21T18:10:08.740

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 – 2011-08-21T21:34:16.710

nth is pronounced 'enth', so you could treat it like it starts with a vowel. – Gareth – 2011-08-22T09:31:30.967