我一直在Mike Vanier's monad tutorial(这很棒),我正在how to use a "State" monad的帖子中进行一些练习。
特别是,他提出了一项练习,其中包括使用factorial
monad编写fibonacci
和State
的函数。我试了一下,然后想出了下面的答案。 (我发现do
符号很混乱,因此我选择了语法)。
我的两个实现都没有特别看到" Haskell-y"并且,为了不将不良做法内化,我想我会问人们如何实现这些功能(使用state
monad)。是否可以更简单地编写此代码(除了切换到do
表示法)?我强烈怀疑是这种情况。
我意识到为此目的使用state
monad有点不切实际,但这纯粹是一种学习练习 - 双关语肯定是有意的。
也就是说,性能并没有那么差:为了计算100000的阶乘(答案是大约21k位数),unfoldr
版本需要~1.2秒(在GHCi中)与〜状态monad版本为1.5秒。
import Control.Monad.State (State, get, put, evalState)
import Data.List (unfoldr)
fibonacci :: Integer -> Integer
fibonacci 0 = 0
fibonacci n = evalState fib_state (1,0,1,n)
fib_state :: State (Integer,Integer,Integer,Integer) Integer
fib_state = get >>=
\s ->
let (p1,p2,ctr,n) = s
in case compare ctr n of
LT -> put (p1+p2, p1, ctr+1, n) >> fib_state
_ -> return p1
factorial :: Integer -> Integer
factorial n = evalState fact_state (n,1)
fact_state :: State (Integer,Integer) Integer
fact_state = get >>=
\s ->
let (n,f) = s
in case n of
0 -> return f
_ -> put (n-1,f*n) >> fact_state
-------------------------------------------------------------------
--Functions below are used only to test output of functions above
factorial' :: Integer -> Integer
factorial' n = product [1..n]
fibonacci' :: Int -> Integer
fibonacci' 0 = 1
fibonacci' 1 = 1
fibonacci' n =
let getFst (a,b,c) = a
in getFst
$ last
$ unfoldr (\(p1,p2,cnt) ->
if cnt == n
then Nothing
else Just ((p1,p2,cnt)
,(p1+p2,p1,cnt+1))
) (1,1,1)
答案 0 :(得分:3)
你的功能似乎比它们需要的要复杂得多,但你有正确的想法。对于阶乘,您需要跟踪的是您乘以的当前数字和到目前为止累积的数字。所以,我们会说State Int Int
是一个计算,它对状态中的当前数字进行操作,并返回到目前为止已成倍增加的数字:
fact_state :: State Int Int
fact_state = get >>= \x -> if x <= 1
then return 1
else (put (x - 1) >> fmap (*x) fact_state)
factorial :: Int -> Int
factorial = evalState fact_state
Prelude Control.Monad.State.Strict Control.Applicative> factorial <$> [1..10]
[1,2,6,24,120,720,5040,40320,362880,3628800]
斐波那契序列相似。你需要保留最后两个数字,以便了解你将要加在一起的内容,以及到目前为止你走了多远:
fibs_state :: State (Int, Int, Int) Int
fibs_state = get >>= \(x1, x2, n) -> if n == 0
then return x1
else (put (x2, x1+x2, n-1) >> fibs_state)
fibonacci n = evalState fibs_state (0, 1, n)
Prelude Control.Monad.State.Strict Control.Applicative> fibonacci <$> [1..10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
答案 1 :(得分:2)
两种风格建议:
\s ->
let (p1,p2,ctr,n) = s
in ...
相当于:
\(p1,p2,ctr,n) -> ...
您case
的{{1}}语句可以使用fib_state
语句撰写:
if