这是一个 simple 解决方案,我想说它比其他答案中的高级解决方案更接近您的 Python 代码。这不是一个惯用解决方案,因为就像您的 Python 解决方案一样,它会使用运行时故障而不是编译器中的类型。
所以,Python 的本质是:返回一个函数或一个 int。在 Haskell 中,不可能根据运行时值返回不同的类型,但是 可以返回可以包含不同数据的类型,包括函数。
data CalcResult = ContinCalc (Int -> String -> CalcResult)
| FinalResult Int
calc :: Int -> String -> CalcResult
calc a "+" = ContinCalc $ \b -> calc (a+b)
calc a "-" = ContinCalc $ \b -> calc (a-b)
calc a "=" = FinalResult a
出于最后会变得清楚的原因,我实际上会提出以下变体,它与典型的 Haskell 不同,不是 curried:
calc :: (Int, String) -> CalcResult
calc (a,"+") = ContinCalc $ \b op -> calc (a+b,op)
calc (a,"-") = ContinCalc $ \b op -> calc (a-b,op)
calc (a,"=") = FinalResult a
现在,您不能只在此之上堆放函数应用程序,因为结果绝不只是一个函数——它只能是一个包装函数。因为应用比处理它们的函数更多的参数似乎是失败的情况,所以结果应该在Maybe monad 中。
contin :: CalcResult -> (Int, String) -> Maybe CalcResult
contin (ContinCalc f) (i,op) = Just $ f i op
contin (FinalResult _) _ = Nothing
为了打印最终结果,让我们定义
printCalcRes :: Maybe CalcResult -> IO ()
printCalcRes (Just (FinalResult r)) = print r
printCalcRes (Just _) = fail "Calculation incomplete"
printCalcRes Nothing = fail "Applied too many arguments"
现在我们可以做
ghci> printCalcRes $ contin (calc (1,"+")) (2,"=")
3
好的,但是对于更长的计算,这将变得非常尴尬。请注意,在两次操作之后我们有一个Maybe CalcResult,所以我们不能再次使用contin。此外,需要向外匹配的括号很麻烦。
幸运的是,Haskell 不是 Lisp 并且支持中缀运算符。而且因为我们无论如何都会在结果中得到Maybe,所以不妨在数据类型中包含失败案例。
那么,完整的解决方案是这样的:
data CalcResult = ContinCalc ((Int,String) -> CalcResult)
| FinalResult Int
| TooManyArguments
calc :: (Int, String) -> CalcResult
calc (a,"+") = ContinCalc $ \(b,op) -> calc (a+b,op)
calc (a,"-") = ContinCalc $ \(b,op) -> calc (a-b,op)
calc (a,"=") = FinalResult a
infixl 9 #
(#) :: CalcResult -> (Int, String) -> CalcResult
ContinCalc f # args = f args
_ # _ = TooManyArguments
printCalcRes :: CalcResult -> IO ()
printCalcRes (FinalResult r) = print r
printCalcRes (ContinCalc _) = fail "Calculation incomplete"
printCalcRes TooManyArguments = fail "Applied too many arguments"
这允许你写
ghci> printCalcRes $ calc (1,"+") # (2,"+") # (3,"-") # (4,"=")
2