管理国家 - SICP第3章 [英] Managing state - chapter 3 of SICP

查看:175
本文介绍了管理国家 - SICP第3章的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在计算机程序的结构和解释中完成并完成Haskell中的练习。前两章没有问题(代码见 github ),但第3章让我觉得很难。



首先讨论管理状态,以银行账户为例。他们通过

 定义了一个函数 make-withdraw  (平衡金额)
(lambda(金额)
(如果(> =余额金额)
(开始(设置!余额( - 余额金额))
余额)
资金不足)))

以便您可以执行以下代码:

 (define w1(make-withdraw 100))
(define w2(make-withdraw 100))

(w1 50)
50

(w2 70)
30

(w2 40)
资金不足

(w1 40)
10

我不确定我可以在Haskell中模拟这个。我首先想到了使用State monad的一些简单函数:

$ p $ import $ Control $ $

类型Cash = Float
类型Account =状态Cash

withdraw ::现金 - >账户(任一字符串现金)
提取金额=国家提款其​​中
提取余额=如果余额> =金额
那么(正确金额,余额 - 金额)
其他(左资金不足,余额)

允许我运行代码

  ghci> runState(do 50; withdraw 40})100 
(剩下资金不足,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]

[右50,右70,左资金不足,右40]

但我不确定如何编写函数 makeWithdraw 。任何建议吗?

解决方案

Scheme代码使用两个状态位偷偷摸摸地进行:一个是变量<隐式c $ c> w1 和 w2 和一个ref-cell;另一个是存储在ref-cell中的(显式)状态。在Haskell中有几种不同的方法来模拟它。例如,我们可能会用 ST 来提取类似的引用细胞技巧:

  makeWithdraw :: Float  - > ST s(Float  - > ST s(或者String Float))
makeWithdraw initialBalance = do
refBalance< - newSTRef initialBalance
return $ \amount - >做
余额< - readSTRef refBalance
让余额'=余额 - 金额
如果余额'< 0
然后返回(左不足资金)
else writeSTRef refBalance余额'>>

这可以让我们做到这一点:

  * 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 |返回[x1,y1,y2,x2]
* Main | :}
[右50.0,右30.0,左资金不足,右10.0]

另一个选项是使两个状态都是显式的,例如通过将每个帐户与唯一的 Int id关联。

 类型AccountNumber = Int 
类型Balance =浮动
数据BankState = BankState
{nextAccountNumber :: AccountNumber
,accountBalance :: Map AccountNumber余额
}

当然,我们基本上会重新执行ref-单元格操作:

  newAccount :: Balance  - > State BankState AccountNumber 
newAccount balance = do
next< - gets nextAccountNumber
modify $ \bs - > bs
{nextAccountNumber = next + 1
,accountBalance =插入下一个余额(accountBalance bs)
}
返回下一个

withdraw :: Account - > ;余额 - > State BankState(任一字符串余额)
提取帐户金额= do
余额< - 从(可能为0。lookup account。accountBalance)
let balance'= balance - amount
if平衡' 0
然后返回(左不足资金)
else修改(\ bs - > bs {accountBalance =插入帐户余额'(accountBalance bs)})>> return(Right balance')

然后让我们写 makeWithdraw

  makeWithDraw :: Balance  - > State BankState(余额 - >国家BankState(任一字符串余额))
makeWithdraw余额=提取< $> newAccount余额


I've been working through in Structure and Interpretation of Computer Programs and completing the exercises in Haskell. The first two chapters were fine (code at github) but Chapter 3 is making me think harder.

It starts by talking about managing state, with the example of a bank account. They define a function make-withdraw by

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

so that you can execute the following code:

(define w1 (make-withdraw 100))
(define w2 (make-withdraw 100))

(w1 50)
50

(w2 70)
30

(w2 40)
"Insufficient funds"

(w1 40)
10

I'm not sure how I can emulate this in Haskell. I first thought to a some simple function using the 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)

which allows me to run the code

ghci> runState (do { withdraw 50; withdraw 40 }) 100
(Left "Insufficient funds",30.0)

but that does something different to the scheme code. Ideally I'd be able to run something like

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]

but I'm not sure how to write the function makeWithdraw. Any advice?

解决方案

The Scheme code is sneakily using two bits of state: one is the (implicit) association between variables w1 and w2 and a ref-cell; the other is the (explicit) state stored in a ref-cell. There's a couple different ways to model this in Haskell. For example, we might pull a similar ref-cell trick with ST:

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')

Which lets us do this:

*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]

Another option is to make both pieces of the state explicit, for example by associating each account with a unique Int id.

type AccountNumber = Int
type Balance = Float
data BankState = BankState
    { nextAccountNumber :: AccountNumber
    , accountBalance :: Map AccountNumber Balance
    }

Of course, we would then basically be re-implementing the ref-cell operations:

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')

Which would then let us write makeWithdraw:

makeWithDraw :: Balance -> State BankState (Balance -> State BankState (Either String Balance))
makeWithdraw balance = withdraw <$> newAccount balance

这篇关于管理国家 - SICP第3章的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆