Изучай Haskell во имя добра! (Липовача) - страница 165

>4037.0

>ghci> solveRPN "90 3.8 -"

>86.2

Отлично, работает!

Добавление новых операторов

Чем ещё хороша наша функция – её можно легко модифицировать для поддержки других операторов. Операторы не обязательно должны быть бинарными. Например, мы можем создать оператор >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, чтобы проверить, было ли чтение успешным.

Из аэропорта в центр

Рассмотрим такую ситуацию. Ваш самолёт только что приземлился в Англии, и у вас арендована машина. В скором времени запланировано совещание, и вам надо добраться из аэропорта Хитроу в Лондон настолько быстро, насколько это возможно (но без риска!).