我正在尝试在Haskell中实现RPN计算器。这是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","+","*"]
)
然后,逐个令牌被推入堆栈。如果它遇到操作员,则操作员处理堆栈中的最后两个项目,然后将产生的值放回堆栈中。遍历整个列表时,堆栈中只剩下一个项目,这就是答案。
这里有一些问题:
在(int $ init accumulator)
中我想取消堆栈中的最后两个元素。除(int $ init accumulator)
之外还有其他选择吗?
代码无法通过编译。 GHC说“输入解析错误”(“
在这一行:| token == "/" = pFunction (
div )
。我怀疑问题可能来自pFunction
。它的参数是一个运算符(或者我可以将其称为函数?)并且我不确定“函数作为函数的参数”在Haskell中是否合法。这合法吗?还有其他选择吗?
我在GHCi做了一些实验,发现了一些奇怪的事情:
Prelude> let plus = (+) Prelude> :t (+) (+) :: Num a => a -> a -> a Prelude> :t plus plus :: Integer -> Integer -> Integer
为什么加号的类型与(+)的类型不同?
感谢您的关注和耐心。 (:
答案 0 :(得分: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`)
您正在使用反引号(`)以使用具有两个参数的函数作为中缀函数。但是,通过在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会尝试推断其类型。然后monomorphism restriction开始。请参阅Type error while writing max function以获取更多信息。
答案 1 :(得分:1)
关于你的第一个问题:
我得到的印象是你正在使用错误的方式。如果在堆栈上推送元素,则应使用:
运算符将其添加到列表中。弹出前两个元素然后变成drop 2 stack
,这更好,我想。
这样做也会更有效率,因为:
是一个恒定时间操作,而++
是第一个参数大小的线性(即你的堆栈大小)。