![](/img/trans.png)
[英]Haskell: Using multiple let and return a value after the do block inside a function
[英]Using the state in a let in a do block in Haskell
我有以下數據結構和功能:
data BTree a = BLeaf | BNode (BTree a) a (BTree a) deriving (Show, Eq)
freshNodesS :: BTree String -> State [String] (BTree String)
freshNodesS BLeaf = return BLeaf
freshNodesS (BNode l m r) = do l' <- freshNodesS l
let m' = getFresh m s
let s' = m : s
r' <- freshNodesS r s'
return (BNode l' m' r')
有一個問題,我實際上想使用freshNodesS l
的狀態,它應該給出(BTree String, [String])
的輸出,但在 do 塊中我不能使用(l', s) <- freshNodesS l
,我看到的唯一選擇是將所有內容都放在 lambda 函數中。 但是有沒有辦法我仍然可以使用 do 符號?
在@chi 所說的之后,我這樣做了:
freshNodesS BLeaf = return BLeaf
freshNodesS (BNode l m r) = do l' <- freshNodesS l
m' <- getFreshS m
r' <- freshNodesS r
return (BNode l' m' r')
getFreshS :: String -> State [String] String
getFreshS x = state $ (\s -> let newx = getFresh x s in (newx, newx: s))
那奏效了。
State
monad 的全部意義在於自動傳遞狀態,而無需您明確地這樣做。 幾乎沒有函數應該獲取s
作為參數,或者返回它。
例如,
let m' = getFresh m s
是可疑的,它可能應該閱讀
m' <- getFresh m
我們會有getFresh :: String -> State [String] String
。
整個代碼應該讀為
do l' <- freshNodesS l
m' <- getFresh m
r' <- freshNodesS r
return (BNode l' m' r')
請注意從未提及s
或s'
。 這應該看起來像命令式代碼,其中每個函數調用都會修改可變狀態變量,即使代碼沒有明確提及。
現在,在getFresh
您將不得不處理狀態,因為沒有辦法解決這個問題。 如果您的State
monad 是標准的,您可以使用get
和put
訪問狀態。 你可能需要類似的東西
getFresh :: String -> State [String] String
getFresh m = do
s <- get -- read the current state
let m' = ... -- compute a fresh name m'
let s' = m' : s -- mark m' as used
put s' -- write the current state
return m'
do-notation 只是語法糖重寫<-
與>>=
綁定和 lambdas 綁定。 如果你可以用一個來寫,你可以用另一個來寫。 所以,如果你認為你可以用 lambda 來寫這個,我鼓勵你這樣做。 然后,將其重寫為使用 do-notation,您將學到一些東西。 但我懷疑您會遇到同樣的絆腳石,因為正如我所說,使用 lambda 代替 do-notation 實際上沒有什么特別之處。
我很難說你想寫什么,因為你沒有為getFresh
提供類型簽名。 有點令人費解的是,這個函數將一個狀態作為直接參數,而不是像程序的其余部分那樣參與 State monad。 我建議重寫它以有簽名
getFresh :: String -> State [String] String
getFresh m = do {...}
您當然必須更改實現,為此我建議您查看get
和put
操作。 但是進行了此更改后,您的freshNodesS
函數將不必對狀態參數進行任何手動線程處理,因為它將完全由狀態機制處理,如預期的那樣:
freshNodesS (BNode l m r) = do
l' <- freshNodesS l
m' <- getFresh m
r' <- freshNodesS r
return (BNode l' m' r')
或者,你可以用 applicative 風格來寫這個:
freshNodesS (BNode l m r) =
BNode <$> freshNodesS l <*> getFresh m <*> freshNodesS r
通過這種方式,您可以清楚地表明,除了共享相同的狀態之外,這三個操作中沒有一個相互依賴,並且您不需要為不做太多事情的變量命名。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.