gpt4 book ai didi

haskell - 管理状态 - SICP 第 3 章

转载 作者:行者123 更新时间:2023-12-04 02:05:54 26 4
gpt4 key购买 nike

我一直在处理 Structure and Interpretation of Computer Programs并在 Haskell 中完成练习。前两章很好(代码为 github),但第 3 章让我更加努力地思考。

首先讨论管理状态,以银行账户为例。他们定义了一个函数make-withdraw经过

(define (make-withdraw balance)
(lambda (amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds")))

这样您就可以执行以下代码:
(define w1 (make-withdraw 100))
(define w2 (make-withdraw 100))

(w1 50)
50

(w2 70)
30

(w2 40)
"Insufficient funds"

(w1 40)
10

我不确定如何在 Haskell 中模拟这一点。我首先想到了一个使用 State monad 的简单函数:
import Control.Monad.State

type Cash = Float
type Account = State Cash

withdraw :: Cash -> Account (Either String Cash)
withdraw amount = state makewithdrawal where
makewithdrawal balance = if balance >= amount
then (Right amount, balance - amount)
else (Left "Insufficient funds", balance)

这允许我运行代码
ghci> runState (do { withdraw 50; withdraw 40 }) 100
(Left "Insufficient funds",30.0)

但这与方案代码有所不同。理想情况下,我可以运行类似的东西
do
w1 <- makeWithdraw 100
w2 <- makeWithdraw 100
x1 <- w1 50
y1 <- w2 70
y2 <- w2 40
x2 <- w1 40
return [x1,y1,y2,x2]

[Right 50,Right 70,Left "Insufficient funds",Right 40]

但我不确定如何编写函数 makeWithdraw .有什么建议吗?

最佳答案

Scheme 代码偷偷地使用了两个状态位:一个是变量之间的(隐式)关联 w1w2和一个引用单元;另一个是存储在引用单元中的(显式)状态。在 Haskell 中有几种不同的方法可以对此进行建模。例如,我们可能会使用 ST 提取类似的 ref-cell 技巧。 :

makeWithdraw :: Float -> ST s (Float -> ST s (Either String Float))
makeWithdraw initialBalance = do
refBalance <- newSTRef initialBalance
return $ \amount -> do
balance <- readSTRef refBalance
let balance' = balance - amount
if balance' < 0
then return (Left "insufficient funds")
else writeSTRef refBalance balance' >> return (Right balance')

这让我们可以这样做:
*Main> :{
*Main| runST $ do
*Main| w1 <- makeWithdraw 100
*Main| w2 <- makeWithdraw 100
*Main| x1 <- w1 50
*Main| y1 <- w2 70
*Main| y2 <- w2 40
*Main| x2 <- w1 40
*Main| return [x1,y1,y2,x2]
*Main| :}
[Right 50.0,Right 30.0,Left "insufficient funds",Right 10.0]

另一种选择是使两个状态都明确,例如通过将每个帐户与唯一的 Int 相关联。 ID。
type AccountNumber = Int
type Balance = Float
data BankState = BankState
{ nextAccountNumber :: AccountNumber
, accountBalance :: Map AccountNumber Balance
}

当然,我们基本上会重新实现 ref-cell 操作:
newAccount :: Balance -> State BankState AccountNumber
newAccount balance = do
next <- gets nextAccountNumber
modify $ \bs -> bs
{ nextAccountNumber = next + 1
, accountBalance = insert next balance (accountBalance bs)
}
return next

withdraw :: Account -> Balance -> State BankState (Either String Balance)
withdraw account amount = do
balance <- gets (fromMaybe 0 . lookup account . accountBalance)
let balance' = balance - amount
if balance' < 0
then return (Left "insufficient funds")
else modify (\bs -> bs { accountBalance = insert account balance' (accountBalance bs) }) >> return (Right balance')

然后让我们写 makeWithdraw :
makeWithDraw :: Balance -> State BankState (Balance -> State BankState (Either String Balance))
makeWithdraw balance = withdraw <$> newAccount balance

关于haskell - 管理状态 - SICP 第 3 章,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10048213/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com