Skip to content

Commit d2a175e

Browse files
authored
Merge pull request #189 from ikostan/exercism-sync/a6145f18d90b7abf
[Sync Iteration] python/wordy/8
2 parents 00feedf + 1ec2b36 commit d2a175e

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed

solutions/python/wordy/8/wordy.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
"""
2+
Parse and evaluate simple math word problems
3+
returning the answer as an integer.
4+
5+
Handle a set of operations, in sequence.
6+
7+
Since these are verbal word problems, evaluate
8+
the expression from left-to-right, ignoring the
9+
typical order of operations.
10+
"""
11+
12+
STR_TO_OPERATOR: dict[str, str] = {
13+
"plus": "+",
14+
"minus": "-",
15+
"multiplied": "*",
16+
"divided": "/",
17+
}
18+
19+
REPLACEABLE: tuple = ("What", "is", "by")
20+
21+
22+
def answer(question: str) -> int:
23+
"""
24+
Evaluate a simple word-based arithmetic question from left to right.
25+
26+
:param question: The input question, e.g., "What is 5 plus 13?"
27+
:type question: str
28+
:returns: The evaluated integer result.
29+
:rtype: int
30+
:raises ValueError: If the operation is unknown or the syntax is invalid.
31+
"""
32+
new_question: list[str] = [
33+
word
34+
for word in question.replace("?", "").split()
35+
if word not in REPLACEABLE
36+
]
37+
# Reduce iteratively:
38+
# evaluate the first three-token slice and fold the result left-to-right.
39+
try:
40+
result, new_question = int(new_question[0]), new_question[1:]
41+
while new_question:
42+
result, new_question = _math_operation(result, new_question)
43+
except ValueError as exc:
44+
if exc.args[0] == "unknown operation":
45+
raise exc
46+
raise ValueError("syntax error") from exc
47+
except IndexError as exc:
48+
raise ValueError("syntax error") from exc
49+
50+
return result
51+
52+
53+
def _math_operation(result: int, question: list[str]) -> tuple[int, list[str]]:
54+
"""
55+
Compute a single binary arithmetic step for the current operator.
56+
57+
Expects the next tokens of the question to begin with the operator word
58+
(e.g. ``plus``, ``minus``, ``multiplied``, ``divided``) followed by the
59+
right-hand side operand. Division uses floor division (``//``) to comply
60+
with the exercise rules.
61+
62+
:param result: Accumulated left-hand value computed so far.
63+
:type result: int
64+
:param question: Remaining tokens starting with the operator then rhs
65+
integer, e.g. ``['plus', '4', ...]``.
66+
:type question: list[str]
67+
:returns: A tuple of the new accumulated result and the remaining tokens
68+
after consuming the operator and rhs.
69+
:rtype: tuple[int, list[str]]
70+
:raises ValueError: If the operator is unknown or the token sequence is
71+
malformed.
72+
"""
73+
math_operator: str = question[0]
74+
# if the question contains an unknown operation.
75+
if (
76+
math_operator not in STR_TO_OPERATOR
77+
and not math_operator.isdigit()
78+
):
79+
raise ValueError("unknown operation")
80+
# if the question is malformed or invalid.
81+
if math_operator.isdigit():
82+
raise ValueError("syntax error")
83+
# if the question is malformed or invalid.
84+
if math_operator in STR_TO_OPERATOR and len(question) == 1:
85+
raise ValueError("syntax error")
86+
87+
if STR_TO_OPERATOR[math_operator] == "+":
88+
result += int(question[1])
89+
90+
if STR_TO_OPERATOR[math_operator] == "-":
91+
result -= int(question[1])
92+
93+
if STR_TO_OPERATOR[math_operator] == "/":
94+
result //= int(question[1])
95+
96+
if STR_TO_OPERATOR[math_operator] == "*":
97+
result *= int(question[1])
98+
99+
return result, question[2:]

0 commit comments

Comments
 (0)