簡體   English   中英

了解單子斐波那契

[英]Understanding Monadic Fibonacci

我正在學習haskell和學習單子。 我已經看過並閱讀了各種教程,並為狀態monad編寫了一些簡單的示例,但是我無法理解以下代碼(摘自Haskell Wiki):

import Control.Monad.State
fib n = flip evalState (0,1) $ do
  forM [0..(n-1)] $ \_ -> do
    (a,b) <- get
    put (b,a+b)
  (a,b) <- get
  return a

我的問題歸結為以下幾點:

  1. 內部do的第一條語句內部是什么,即(a,b)<-get結果是什么。 對於某些具體示例, ab的值是多少。
  2. 您為什么要在這里使用州立monad?

在此示例中,狀態是一對,其中包含在序列中生成的前兩個數字。 最初(0, 1)提供給evalState

get的類型是MonadState sm => ms因此在內部do塊中

(a, b) <- get

獲取狀態對並將ab分別綁定到第一和第二個元素。 然后在以下put更新狀態。

因此,狀態為:

(0, 1), (1, 1), (1, 2), (3, 2), (3, 5), ...

外層

(a, b) <- get
return a

解壓縮最終狀態值並返回第一個元素。

首先讓我們弄清楚正在使用的斐波那契算法。 這個想法是從元組(0, 1) ,然后找到下一個(1, 0 + 1) ,下一個是(1, 1 + 1)(2, 2 + 1)(3, 3 + 2) ,依此類推。 通常,該步驟為\\(a, b) -> (b, a + b) 您可以看到在這些元組中是斐波那契數。

內部do的第一個語句的內容是什么,即(a,b)<-得到的結果是什么?

Haskell沒有語句,只有表達式。

y <- x不是完整的表達式。 它類似於x >>= \\y ->

y <- x
m

是一個完整的表達式,等效於x >>= \\y -> m 不以y <- n形式出現的行n等效於_ <- n (不包括let行以及可能我忘記的其他行)。

使用此,我們可以對糖做注釋符號。

fib n =
  flip evalState (0, 1)
  ( forM
      [0..(n-1)]
      (\_ -> get >>= (\(a, b) -> put (b, a + b)))
    >>= (\_ -> get >>= (\(a, b) -> return a)))
  )

現在只需要了解>>=returngetput等。

狀態實際上只是s -> (s, a)類型s -> (s, a)函數。 它們采用初始狀態,並產生下一個狀態加上一些其他值。

m >>= n aka“ bind”的類型為Monad m => ma -> (a -> mb) -> mb 然后,如果我們的Monad是State s ,則與:

m >>= n ::
     (     s -> (s, a))
  -> (a -> s -> (s, b))
  -> (     s -> (s, b))

m返回的a必須傳遞給n 我們還能猜到什么? 我們希望狀態也會傳遞,因此m返回的狀態也必須傳遞給n 函數m >>= n必須返回狀態和值n回報。 然后,我們知道如何實現綁定:

m >>= n = uncurry (flip n) . m

return :: Monad m => a -> ma然后等於return :: Monad m => a -> ma return :: a -> s -> (s, a)

return = flip (,)

get :: State ss等於get :: s -> (s, s)

get = join (,)

put :: s -> State s ()put :: s -> s -> (s, ())

put s _ = (s, ())

evalState :: s -> State sa -> a evalState :: s -> (s -> (s, a)) -> a evalState :: s -> State sa -> aevalState :: s -> (s -> (s, a)) -> a

evalState s f = snd (f s)

您可以展開所有定義,然后准確查看示例中發生的情況。 只是直覺就足夠了。

forM
  [0..(n-1)]
  (\_ -> get >>= (\(a, b) -> put (b, a + b)))

我們不關心數字從0n - 1所以第一個參數被刪除了。 get檢索當前狀態,然后put寫入新狀態。 我們這樣做n次。

>>= (\_ -> get >>= (\(a, b) -> return a)))

我們不在乎累計值(單位),因此刪除了第一個參數。 然后,我們獲得當前狀態並投影該對中的第一個元素。 這是我們正在尋找的最終答案。

flip evalState (0, 1) …

最后,我們從初始狀態(0, 1)開始運行。

我們可以對該實現進行一些清理。 首先,我們不關心的范圍[0..(n-1)]中,我們只關心重復動作n次。 一種更直接的方法是:

replicateM n (get >>= \(a, b) -> put (b, a + b))

結果是未使用的單位列表,因此更有效的版本是:

replicateM_ n (get >>= \(a, b) -> put (b, a + b))

已經有用於通用模式的get的功能,其后跟名為modifyput ,其定義為\\f -> get >>= put . f \\f -> get >>= put . f 因此:

replicateM_ n (modify (\(a, b) -> (b, a + b)))

然后是部分:

>>= (\_ -> get >>= (\(a, b) -> return a)))

每當我們不關心先前的結果時,都可以使用>>

>> get >>= (\(a, b) -> return a))

這是:

>> get >>= return . fst

m >>= return . f m >>= return . f簡化為fmap fm

>> fmap fst get

現在,我們總共有:

fib n =
  evalState
  (  replicateM_ n (modify (\(a, b) -> (b, a + b)))
  >> fmap fst get
  )
  (0, 1)

我們還可以將其用於比較:

fib n =
  fst
  ( evalState
    (  replicateM_ n (modify (\(a, b) -> (b, a + b)))
    >> get
    )
    (0, 1)
  )

然后因為我很傻:

fib =
  fst
  . flip evalState (0, 1)
  . (>> get)
  . flip replicateM_ (modify (snd &&& uncurry (+)))

您為什么要在這里使用州立monad?

你不會的 這很清楚,因為我們只使用狀態值; 另一個值始終是單位並被丟棄。 換句話說,我們一開始只需要n (即找到哪個斐波那契數),之后我們只需要累積的元組即可。

有時您認為具有類似h . g . f的字符串h . g . f h . g . f h . g . f但您想發送兩個參數而不是一個。 那就是State可能適用的時候。

如果某些函數讀取狀態,某些函數寫入狀態(第二個參數),或者兩者都做,則State符合要求。 如果只有讀者,則使用Reader ;如果只有作家,則使用Writer

我們可以更改示例以更好地利用State Monad。 我將使元組消失!

fib =
  flip evalState 0
  . foldr (=<<) (return 1)
  . flip replicate (\x -> get >>= \y -> put x $> x + y)

因此,文檔狀態為: get :: ms -- Return the state from the internals of the monad (請參見此處 )。

但是我非常記得,當我試圖將自己的頭纏在莫納德州時,這並沒有太大幫助。

我只能建議在ghci中使用:i:t並測試不同的子表達式。 只是為了感受一下。 有點像這樣:

 import Control.Monad.State.Lazy 

 runState (get) 0
 runState (get >>= \x -> put (x+1)) 0
 :t return 1 :: State Int Int
 runState (return 1) 0
 runState (return 1 >>= \x -> (get >>= \y -> return (x+y))) 0 

 -- Keeping a pair of (predecessor/current) in the state:
 let f = (get >>= (\(a,b) -> put (b,a+b))) :: State  (Int, Int) ()
 runState (f >> f >> f >> f >> f >> f) (0,1) 

 -- only keeping the predecessor in the state:
 let f x = (get >>= (\y -> put x >> return (x+y))) :: State Int Int
 runState (return 1 >>= f >>= f >>= f >>= f >>= f >>= f) 0 

還玩弄modifyrunStateevalStateexecState

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM