Проблемы с Haskell в реализации RPN caculator

Я пытаюсь внедрить RPN caculator в Haskell. Это упражнение от Learn You a Haskell. Вот мой код:

import Data.List
solveRPN :: String -> Int
solveRPN str = head $ foldl putStack [] (words str) 
         where putStack accumulator token 
            | token == "+" = pFunction (+)
            | token == "-" = pFunction (-)
            | token == "*" = pFunction (*)
            | token == "/" = pFunction (`div`)
            | otherwise    = accumulator ++ [read token :: Float]
            where pFunction function =  (int $ init accumulator) ++ [function argu1 argu2]
                  argu1 = last accumulator
                  argu2 = last $ init accumulator

Функция solveRPN сначала разбейте строку на токены. (Например:"4 3 2 + *"->["4","3","2","+","*"]) Затем один за другим токены помещаются в стек. Если он встречает оператор, последние два элемента в стеке обрабатываются оператором, а полученное значение затем помещается обратно в стек. Когда весь список пройден, в стеке остается только один элемент, и это ответ.

Здесь есть некоторые проблемы:

  1. В (int $ init accumulator) Я хочу отменить последние два элемента в стеке. Есть ли альтернатива (int $ init accumulator)?

  2. Код не может пройти компиляцию. GHC сказал "ошибка разбора на входе ("
    на этой линии: | token == "/" = pFunction (ДИВ), Я подозреваю, что проблема может исходить от pFunction, Его параметр является оператором (или я могу назвать это функцией?), И я не уверен, является ли "функция как параметр функции" допустимой в Haskell. Это законно? Есть ли альтернатива?

  3. Я провел несколько экспериментов в GHCi и обнаружил нечто странное:

Prelude> let plus = (+) 
Prelude> :t (+) 
(+) :: Num a => a -> a -> a
Prelude> :t plus 
plus :: Integer -> Integer -> Integer

Почему тип плюса отличается от типа (+)?

Спасибо за ваше внимание и терпение. (:

2 ответа

В (int $ init accumulator)

Ты имел ввиду init $ init accumulator? При этом, вы можете написать свой собственный dropLast2 функция, которая делает то же самое, что и init . init но обходит список только один раз, что-то вроде

dropLast2 :: [a] -> [a]
dropLast2 []       = []
dropLast2 [_]      = []
dropLast2 [_,_]    = []
dropLast2 (x:xs) = x : dropLast2 xs

Код не может пройти компиляцию.

        | token == "/" = pFunction (`div`)

Вы используете backticks (`) для использования функций с двумя аргументами в качестве инфиксных функций. Тем не менее, используя круглые скобки вокруг div вы пытаетесь отменить его сразу, что немного отключено и является ошибкой парсера. Просто использовать

        | token == "/" = pFunction div

вместо. Тем не менее, есть одна важная вещь. divтип

div :: Integral a => a -> a -> a

Тем не менее, ваш аккумулятор является списком Float, div не может работать на тех. Тем не мение, Float является примером Fractional класс, так что вы можете просто использовать (/):

(/) :: Fractional a => a -> a -> a

и, следовательно, получить

        | token == "/" = pFunction (/)

Я провел несколько экспериментов в GHCi и нашел что-то странное

(+) является частью Num учебный класс. Ваш plus не является частью какой-либо функции, и GHC пытается определить ее тип. И тогда наступает ограничение мономорфизма. См. Ошибка типа при написании функции max для получения дополнительной информации.

По поводу вашего первого вопроса:

У меня создается впечатление, что вы используете списки неправильно. Если вы помещаете элемент в стек, вы должны добавить его к списку, используя : оператор. Появление двух верхних элементов становится drop 2 stackчто гораздо приятнее, я думаю.

Делать это таким образом также было бы более эффективным, так как : это операция с постоянным временем, а ++ является линейным по размеру первого аргумента (т.е. размер вашего стека).

Другие вопросы по тегам