5
\$\begingroup\$

I have 25 branches but I'm only allowed to have 12 branches maximum. The objective of this task was to cancel out unnecessary punctuation, find the common elements that exist in the sentence and dictionary and then to translate the result into English into the other language or the other language to English, depending on what we are given. How do I lessen the number of branches of code I have?

sentence = "?hello boolean bring, !mango! and, country ban,ana wish yum  apple!" 
dictionary = "a aa apple banana pear strawberry mango country wish boolean bring" 
punctuation = "?!," 
language = "English"

"""answer = buwulean bringah mango countryeeh wishey apple"""

"""The above lines aren't in my final code, this is just for information 
about what I'm trying to solve for.""" 

def translate(sentence, dictionary, punctuation, language):
    """R"""

    sentence = ' '.join([x.strip(punctuation) for x in sentence.split()]) 
    """Removing unnecessary punctuation"""  

    dictionary = dictionary.split()
    sentence = sentence.split()
    result = []

    for element in sentence:
        if element in dictionary:
            result.append(element)

    result2 = []

    for word in result:
        if language == "English":
            if word[-1] == "y":
                word = "{}eeh".format(word)
                result2.append(word)
            elif word[-3:] == "ing":
                word = "{}ah".format(word)
                result2.append(word)
            elif word[-2:] == "sh":
                word = "{}ey".format(word) 
                result2.append(word)
            elif "oo" in word:
                word = word.replace("oo", "uwu")
                result2.append(word)
            elif word == "is":
                word = "oy"
                result2.append(word)
            elif word == "are":
                word = "argh"
                result2.append(word)
            elif word == "he" or word == "she" or word == "it":
                word = "hana"
                result2.append(word)
            elif word == "his" or word == "her":
                word = "hami"
                result2.append(word)
            elif word == "ham" or word == "sausage" or word == "bacon":
                word = "{}!".format(word)
                result2.append(word)        
        else:
            if word[-3:] == "eeh":
                word = word[:-3]
                result2.append(word)
            elif word[-2:] == "ah":
                word = word[:-2]
                result2.append(word)
            elif word[-2] == "ey":
                word = word[:-2]
                result2.append(word)
            elif "uwu" in word:
                word = word.replace("uwu", "oo")
                result2.append(word)
            elif word == "oy":
                word = "is"
                result2.append(word)
            elif word == "argh":
                word = "are"
                result2.append(word)
            elif word == "hana":
                word = "he"
                result2.append(word)
            elif word == "hami":
                word = "his"
                result2.append(word)
            elif word == "ham!":
                word = "ham"
                result2.append(word)
            elif word == "sausage!":
                word = "sausage"
                result2.append(word)
            elif word == "bacon!":
                word = "bacon"
                result2.append(word)
            else:
                result2.append(word)
    return " ".join(result2)

translated = translate(sentence, dictionary, punctuation, language)
print(translated)

"""The last two lines are also not in my final code"""
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

Focusing on the specific part of the code that handles translations for English, here's a way of doing it with a dictionary and a function table:

english_swaps = {
    # Dictionary of 1:1 word replacements.
    "is": "oy",
    "are": "argh",
    "he": "hana",
    "she": "hana",
    "it": "hana",
    "his": "hami",
    "her": "hami",
    "ham": "ham!",
    "sausage": "sausage!",
    "bacon": "bacon!",
}

english_rules = [
    # List of arbitrary functions to transform a word.
    lambda w: f"{w}eeh" if w[-1] == "y" else None,
    lambda w: f"{w}ah" if w[-3:] == "ing" else None,
    lambda w: f"{w}ey" if w[-2:] == "sh" else None,
    lambda w: w.replace("oo", "uwu") if "oo" in w else None,
    lambda w: english_swaps.get(w, None),
]

def translate(word: str) -> str:
    if language == "English":
        for rule in english_rules:
            # Attempt to apply each rule in turn, using the first one to match.
            translated = rule(word)
            if translated is not None:
                return translated
        return word
    else:
        # ...

print(" ".join(
    translate(w) for w in "he is eating sausage and loving every bit of it".split()
))
# prints: hana oy eatingah sausage! and lovingah everyeeh bit of hana

You could apply this same pattern to have a dictionary and function table for each language, and put those into another dictionary that maps each language to a function table, so that ultimately you could do something like:

for rule in rules[language]:
   translated = rule(word)
   ... etc ...
\$\endgroup\$
1
  • 1
    \$\begingroup\$ I think the first four lambdas in english_rules are more easily expressed with re.sub, e.g. lambda w: re.sub('y$', 'yeeh', w) \$\endgroup\$
    – Reinderien
    Commented Apr 20, 2020 at 3:02
2
\$\begingroup\$

Sam has address some of the individual word translations. Let's look at some of the other code.

split/join/split

sentence = ' '.join([x.strip(punctuation) for x in sentence.split()])
...
sentence = sentence.split()

Here, you are joining splitting the sentence into words, stripping out punctuation, joining the words back into a sentence, and then splitting the sentence back into words. Those last two steps seem unnecessary. You could simply have:

sentence = [word.strip(punctuation) for word in sentence.split()]

This isn't exactly the same. If you have stand alone punctuation in the sentence, it will leave an empty string in the list, whereas joining and resplitting will eliminate those. But due to the next step, filtering on a dictionary, it doesn't matter.

in dictionary

dictionary = dictionary.split()

for element in sentence:
    if element in dictionary:
        ...

You are looking up elements in a list(...), which is an \$O(N)\$ operation. The in operator must start at the beginning of the list, comparing element with successive elements, until it finds the element or reaches the end of the list.

Containment tests are much faster when performed on sets. The input is hashed to determine which bin to look for the element in. The result is closer to \$O(1)\$ time complexity. In short, it is usually faster.

dictionary = set(dictionary.split())

for element in sentence:
    if element in dictionary:
        ...

Not a big change for the resulting speed improvement.

List comprehension

result = []

for element in sentence:
    if element in dictionary:
        result.append(element)

Creating a list, and then adding elements to the list one-by-one is inefficient. The list must be continuously resized, where each resize may involve allocating a larger chunk of memory and copying elements to the new memory location.

Copying all of the elements that pass some criteria from one list to another list is a common operation, and Python has created a syntax for doing this efficiently.

result = [ element for element in sentence if element in dictionary ]

The Python interpreter can "guess" that the size of result will be at most the size of the sentence list. One allocation, copy the data, then reallocate to the actual required size. Faster code, and less code as well; double win.

Type consistency

In your code, what is the type of the data held in the variables sentence and dictionary? Initially, they hold strings, but later they hold lists. This variable type mutation makes the code harder to analyze, both by code analysis tools & by humans. Try to use separate variables for separate concepts. A sentence would be a string; if split on white-space, you get words. Use meaningful names too. While x may be a perfectly fine variable name for coordinate systems, it does not convey any meaning when used for part of a sentence.

valid_words = set(dictionary.split())
words = [fragment.strip(punctuation) for fragment in sentence.split()]
words = [word for word in words if word in valid_words]

Generator stream

Instead of performing the transformation in steps, at each step fully building lists of temporary results using list comprehension, you could also create a pipeline of generator expressions that fully process the translation of each word before fetching the next and accumulating the results at the end.

Borrowing Sam Stafford's english_rules ...

rules = { 'English': english_rules }

def translate(sentence: str, dictionary: str, punctuation: str, language: str) -> str:
    """
    Translate words in ``sentence`` that are found in the ``dictionary``, 
    according of the ``language`` rules, after removing unnecessary ``punctuation``.
    """

    def apply_rules(word, rules):
        for rule in rules:
            translated = rule(word)
            if translated:
                return translated
        return word

    valid_words = set(dictionary.split())

    words = (fragment.strip(punctuation) for fragment in sentence.split())
    words = (word for word in words if word in valid_words)
    words = map(functools.partial(apply_rules, rules=rules[language]), words)

    return " ".join(words)

if __name__ == '__main__':
    sentence = "?hello boolean bring, !mango! and, country ban,ana wish yum  apple!" 
    dictionary = "a aa apple banana pear strawberry mango country wish boolean bring" 
    punctuation = "?!," 
    language = "English"
    print(translate(sentence, dictionary, punctuation, language))

Result:

buwulean bringah mango countryeeh wishey apple

\$\endgroup\$

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.