functions :: (Floating a) => [(String, a -> a)]
functions = [("exp", exp), ("sqrt", sqrt), ("log", log), ("sin", sin), ("tan", tan), ("cos", cos),
("asin", asin), ("atan", atan), ("acos", acos), ("sinh", sinh), ("tanh", tanh), ("cosh", cosh),
("asinh", asinh), ("atanh", atanh), ("acosh", acosh), ("abs", abs)]
Очень красиво, я считаю. Никакого лишнего мусора.Таблица операторов уложилась в 10 строчек:
divide expr loc l r | (r == 0.0) = throwError CalculatorException "Evaluation error. Division by zero" expr loc
divide _ _ l r = l / r
const2 = const . const
binaryOperators :: (Eq a, Floating a) => [BinaryOperatorInfo a]
binaryOperators = [
BinaryOperatorInfo Assign '=' 0 undefined,
BinaryOperatorInfo Plus '+' 1 (const2 (+)),
BinaryOperatorInfo Minus '-' 1 (const2 (-)),
BinaryOperatorInfo Multiply '*' 2 (const2 (*)),
BinaryOperatorInfo Divide '/' 2 divide,
BinaryOperatorInfo Power '^' 3 (const2 (**))]
Заметьте, в таблице указаны приоритеты операторов, которые учитывает парсер. При добавлении нового оператора код парсера менять не нужно – он универсален.Ну и таблица констант. 2 строчки:
constants :: (Floating a) => [(String, a)]
constants = [("pi", pi), ("e", exp 1)]
Как видите, я в коде решил попробовать исключения. Впечатление двоякое: с одной стороны, он делает код простым и избавляет от необходимости протаскивать значение наверх по стеку функций, с другой – исключения в чистом языке являются несколько чужеродными. Кроме того, возникли проблемы с ленивостью, точнее непонимания мною ленивых вычислений, в результате чего мой первоначальный код не ловил исключения, несмотря на то что был обёрнут в catch-блок. Проблема решилась заменой функции return на функцию evaluate. В общем, вывод таков – исключения лучше использовать только при работе с IO. В чистые функции их не стоит пихать.
Вопрос читателям: нормально ли оформлен код? Я что-то всё никак не могу понять, как лучше всего вставлять код в посты с красивой подсветкой и рамкой.
Комментариев нет:
Отправить комментарий