Syllabify English words - kind of

You're tasked with writing a program that syllabifies words in a string of text, by separating them with a hyphen. That would be a lot of work, so you want to skip some parts, mainly because you don't want to have a table of the pronunciations required for perfect algorithm. You also want to make it as short (and thus unreadable and unmaintainable) as possible as a revenge for being given this assignment.

You have two choices:

• Write a program that takes the string from STDIN and outputs the result to STDOUT.
• Write a function that takes the string as a single parameter, and returns the result.

Specification

• In this case, string means any string-like construct in your language of choice (byte arrays, character arrays, strings...).
• Vowels are a, e, i, o, u
• The string given has 1 <= n <= 10 words, where each has a length between 1 - 30 characters, inclusive.. Your output should be those words hyphenated.
• All letters are lowercase and words are always separated with spaces. Thus, the input consists of characters [a-z ]
• Apply rules in order of importance.
• When a word is divided, start again with the right half of the word.

Rules for syllabification, in order of importance

Two consecutive same vowels count as one (ie. feet has only one vowel, but beat and finding have two). Every syllable has exactly one vowel, thus there is one syllable for each vowel.

1. If the whole word has only four letters, return it unchanged. (skip this for the rest of the word)
2. If the word has only one vowel, return the word unchanged.
3. If the word has two consecutive vowels, divide between them (ie. diaspora -> di-as-po-ra)
4. When two or more consonants come between two vowels (same or different), divide after the first consonant (ie. sis-ter) unless the consonant part is ck, in which case divide the word after it. (ie. nickel -> nick-el)
5. When a y comes between two vowels, divide the word after it (e.g. paying -> pay-ing).
6. When one consonant comes between two vowels (same or different), divide before the consonant (ie. dra-gon)
7. Return the word unchanged if no division could be done.

I chose these rules, because they can be applied recursively without problems and require no pronunciation tables. Thus, they are not accurate, and for example rule #5 is often not correct. In general case though, it is.

Example

In:  hello world
Out: hel-lo world

In:  have a nice day
Out: have a nice day

In:  pour some nickel and xenon there
Out: pour some nick-el and xe-non the-re

• Are you sure about x-e-non? Reference for rule #4? Jul 22, 2014 at 13:40
• @JanDvorak "When a word is divided, start again with the right half of the word.", followed by rule #6. Jul 22, 2014 at 13:42
• I mean, shouldn't rule #4 only split between syllables? Jul 22, 2014 at 13:43
• Rule #1 deals with four letters words. What about words with less than four letters? e.g. lua Jul 22, 2014 at 18:48
• @DigitalTrauma They get prosessed normally, but rarely have two syllables. Jul 22, 2014 at 18:58

Lua, 292

Lua might not have been the best language to do this in, but it works. It pretty much flows like the question asked. The rules are mainly in order with some optimizations: #2 is skipped (it isn't needed unless there is a one vowel word with "ck" at the beginning), and the ck and y rules are gone through before the rest of #4 and #6, which are combined. Since some vowels in the word need to be captured twice (after one hyphen and before another), that search is performed twice.

i=io.read()v="([aeiou])"for s in i:gfind("%l+ ?")do
if s:len()~=4 then
s=s:gsub(v..v,function(x,y)if x==y then return x..y;end;return x.."-"..y;end)s=s:gsub("ck"..v,"ck-%1")s=s:gsub(v.."y"..v,"%1y-%2")for b=1,2 do
s=s:gsub(v.."([^aeiou\-]?)([^aeiou\-]+)"..v,"%1%2-%3%4")end
end
io.write(s)end


Ungolfed

function checkEquals(x,y)
if x==y then
return x..y
end
return x.."-"..y
end
v="([aeiou])"
for s in i:gfind("%l+ ?") do
if s:len()~=4 then
s=s:gsub(v..v,checkEquals)
s=s:gsub("ck"..v,"ck-%1")
s=s:gsub(v.."y"..v,"%1y-%2")
for b=1,2 do
s=s:gsub(v.."([^aeiou\-]?)([^aeiou\-]+)"..v,"%1%2-%3%4")
end
end
io.write(s)
end


Test it here: http://ideone.com/g57TzA

• I don't have ruby, but it seems fine indeed. Jul 22, 2014 at 17:17

Ruby, 144 bytes

If we're going for unmantainable, how about a single giant regex?

puts gets.split.map {|w| w.scan(/(^.{4}$|[^aeiou]*([aeiou])\2?((?=[^aeiouy]?[aeiou])|ck|[^aeiou]((?=.*[aeiou])|.*$)|$))/).map(&:first)*'-'}*' '  some output: echo "hello world" | ruby syllable.rb hel-lo world echo "have a nice day" | ruby syllable.rb have a nice day echo "pour some nickel and xenon in there" | ruby syllable.rb pour some nick-el and xe-non in the-re echo "diaspora dragon paying sister hemlock happy quicksilver" | ruby syllable.rb di-as-po-ra dra-gon pay-ing sis-ter hem-lock happy qu-ick-sil-ver  Bash+coreultils, 173 bytes I think I have all the latest rule changes: v=aeiou r="[$v])/\1-\2/g"
s=s/\([$v] e="$s[^$v-])([^$v-]+$r " tr \ \\n|sed -r "/^([a-z]{4}|[^$v]*[$v][^$v]*)$/bx$s)($r${s}ck)($r$e$e${s}y)($r$s)([^$v-]$r
:x"|tr \\n \


Note the last character of the last line is a   (space).

I think this sufficiently satisfies "unreadable and unmaintainable" ;-)

Takes input from STDIN.

Mostly straight regex substitution. The first line of the sed expression matches for rules 1 and 2, then simply jumps to the :x label at the end of the expression.

The trs at the start and end of the pipeline make the words newline-separated, so they are easier for sed to deal with. I was hoping to do and all sed answer, but this way is simpler and easier.

Example:

$./sylabify.sh <<< "diaspora nickel sister dragon hello world have a nice day pour some nickel and xenon there paying tricks quicksilver" di-as-po-ra nick-el sis-ter dra-gon hel-lo world have a nice day pour some nick-el and xe-non the-re pay-ing tricks qu-ic-ksil-ver$

• Argh, I keep forgetting the rule #3 changed. It's fine. Jul 22, 2014 at 18:30