4
\$\begingroup\$

All the usual stuff. Style, substance, algorithm please! I'm vaguely considering plugging it into a bot, hence the lacklustre catching of exceptions right now.

import requests
import sys

def get_num_syllables(value):
    try:
        res = requests.get("http://www.thefreedictionary.com/" + value)
    except requests.exceptions.ConnectionError as e:
        return -1

    for word in res.text.split():
        if "<h2>" not in word:
            continue
        word = word[word.find("<h2>")+ 4:word.find("</h2>")]

        if '·' in word:
            return word.count('·') + 1
    if "Word not found in the Dictionary and Encyclopedia." in res.text:
        return -1
    return 1

def haikuify(sentence):
    sentence = sentence.strip()
    num_syllables = []
    for word in sentence.split():
        num_syllables.append(get_num_syllables(word))

    if -1 in num_syllables:
        raise SyntaxError("Someone can't spell - can't find a word from the sentence in the dictionary")

    haiku_form = [5, 7, 5]
    haiku_indices = []
    i = -1
    run = 0
    line = 0

    for value in num_syllables:
        run += value
        if run is haiku_form[line]:
            haiku_indices.append(i)
            line += 1
            run = 0
        elif run > haiku_form[line]:
            raise SyntaxError("Not a haiku")
            break
        i += 1

    haiku_indices.append(len([len(x) for x in sentence.split()]) - 1)

    if len(haiku_indices) is not 3:
        raise SyntaxError("Not a haiku")

    i = -1
    line = 0
    this_line = ""
    haiku = []
    for word in sentence.split():
        word = word.replace(" ", "")
        if not word:
            continue

        this_line += " " + word
        i += 1
        if i is haiku_indices[line]:
            haiku.append(this_line)
            this_line = ""
            line += 1

    return haiku

if __name__ == "__main__":
    sentence = input("Sentence to haikuify: ")
    haiku = []
    try:
        haiku = haikuify(sentence)
    except SyntaxError as err:
        print(err.msg)
    print(haiku)
\$\endgroup\$
1
  • 2
    \$\begingroup\$ A usage example would really help. Also maybe help the user more when saying This is not a haiku and tell them how many syllables were missing (or too much). \$\endgroup\$
    – Graipher
    Sep 14, 2016 at 15:39

1 Answer 1

2
\$\begingroup\$

You should be more helpful to the user, both when the entered sentence can't be made into a haiku (too short or too long) and when not finding the word in the dictionary. At least tell which word was not found in the dictionary.

Also when thefreedictionary.com is not reachable (for whatever reasons), your code will insult the user by telling him he can't spell.

You dictionary lookup fails for some words:

  • "sparrow's", or any possessive form (raises SyntaxError)
  • "banging", or any gerund form. (gives the syllables of the infinitive instead)
  • Any punctuation (also raises SyntaxError)

I tested your program with this haiku from wikipedia:

Snow in my shoe

Abandoned

Sparrow's nest

— Jack Kerouac, collected in Book of Haikus, 2003

And this:

Whitecaps on the bay:

A broken signboard banging

In the April wind.

— Richard Wright, collected in Haiku: This Other World, 1998

The first raises a SyntaxError and the second puts In on the end of the second line because of the gerund counting as one less syllable.

What your code also does not catch is that thefreedictionary puts a flood mechanism into place after some time. I found this out by testing my own script. In this case the fifth request in a row returns with HTTP code 403 (Access forbidden).


How I would write it:

import requests
import sys
import re


def get_num_syllables(value):
    if value.endswith("ing"):
        return get_num_syllables(value[:-3]) + 1
    try:
        res = requests.get("http://www.thefreedictionary.com/" + value)
    except requests.exceptions.ConnectionError as e:
        print("Can't connect to dictionary website: http://www.thefreedictionary.com/" + value)
        sys.exit(1)
    if res.status_code != 200:
        raise requests.exceptions.ConnectionError("Can't connect to dictionary website: http://www.thefreedictionary.com/{}, HTTP code {}".format(value, res.status_code))
    value_re = re.compile(value)
    for word in res.text.split():
        if "<h2>" not in word:
            continue
        word = word[word.find("<h2>") + 4:word.find("</h2>")].lower()
        if '·' in word:
            match = re.match(word.replace('·', ''), value)
            if not match:
                continue
            return word.count('·') + 1
    not_founds = ("Word not found in the Dictionary and Encyclopedia.",
                 "is not available in the general English dictionary")
    if any(not_found in res.text for not_found in not_founds):
        raise SyntaxError("Can't find word in dictionary: {}".format(value))
    return 1


def get_num_syllables_all(words):
    syllables = {}
    for word in words:
        word = word.lower()
        if word in syllables:
            continue
        syllables[word] = get_num_syllables(word)
    return syllables


def haiku(words, haiku_form):
    # words = "Whitecaps on the bay A broken signboard banging In the April wind".split()
    num_syllables = get_num_syllables_all(words)

    lines = [[] for _ in range(len(haiku_form))]
    count, line = 0, 0
    for word in words:
        count += num_syllables[word.lower()]
        if count > haiku_form[line]:
            raise SyntaxError("Not possible to split word at syllable boundary: {}, {}".format(word, syllables))
        lines[line].append(word)
        if count == haiku_form[line]:
            line += 1
            count = 0

    # validate the haiku
    for i, line in enumerate(haiku_form):
        sum_line = sum(num_syllables[word] for word in lines[i])
        if sum_line > line:
            raise SyntaxError("Too many syllables in line {}. Got {}, want {}".format(i, sum_line, line))
        elif sum_line < line:
            raise SyntaxError("Not enough syllables in line {}. Got {}, want {}".format(i, sum_line, line))
    return "\n".join(" ".join(line) for line in lines)

if __name__ == '__main__':
    haiku_form = (5, 7, 5)
    words = input("Please enter the words to use: ").strip().split()
    try:
        print(haiku(words, haiku_form))
    except SyntaxError as e:
        print(e)
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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