Добавление новых операторов
Чем ещё хороша наша функция – её можно легко модифицировать для поддержки других операторов. Операторы не обязательно должны быть бинарными. Например, мы можем создать оператор >log
, который выталкивает из стека одно число и заталкивает обратно его логарифм. Также можно создать тернарный оператор, который будет извлекать из стека три числа и помещать обратно результат. Или, к примеру, реализовать оператор >sum
, который будет поднимать все числа из стека и суммировать их.
Давайте изменим нашу функцию так, чтобы она понимала ещё несколько операторов.
>solveRPN :: String –> Double
>solveRPN = head . foldl foldingFunction [] . words
> where
> foldingFunction (x:y:ys) "*" = (x * y):ys
> foldingFunction (x:y:ys) "+" = (x + y):ys
> foldingFunction (x:y:ys) "–" = (y – x):ys
> foldingFunction (x:y:ys) "/" = (y / x):ys
> foldingFunction (x:y:ys) "^" = (y ** x):ys
> foldingFunction (x:xs) "ln" = log x:xs
> foldingFunction xs "sum" = [sum xs]
> foldingFunction xs numberString = read numberString:xs
Прекрасно. Здесь >/
– это, конечно же, деление, и >**
– возведение в степень для действительных чисел. Для логарифма мы осуществляем сравнение с образцом для одного элемента и «хвоста» стека, потому что нам нужен только один элемент для вычисления натурального логарифма. Для оператора суммы возвращаем стек из одного элемента, который равен сумме элементов, находившихся в стеке до этого.
>ghci> solveRPN "2.7 ln"
>0.9932517730102834
>ghci> solveRPN "10 10 10 10 sum 4 /"
>10.0
>ghci> solveRPN "10 10 10 10 10 sum 4 /"
>12.5
>ghci> solveRPN "10 2 ^"
>100.0
На мой взгляд, это делает функцию, способную вычислять произвольное выражение в обратной польской записи с дробными числами, которое может быть расширено 10 строчками кода, просто-таки расчудесной.
ПРИМЕЧАНИЕ. Как можно заметить, функция не устойчива к ошибкам. Если передать ей бессмысленный вход, она вывалится с ошибкой. Мы сделаем её устойчивой к ошибкам, определив её тип как >solveRPN :: String –> Maybe Double
, как только разберёмся с монадами (они не страшные, честно!). Можно было бы написать безопасную версию функции прямо сейчас, но довольно-таки скучным будет сравнение с >Nothing
на каждом шаге. Впрочем, если у вас есть желание, попробуйте! Подсказка: можете использовать функцию >reads
, чтобы проверить, было ли чтение успешным.