Haskell 中的有条件状态更改
Conditional change of State in Haskell
我不知道如何在 Haskell 中对 State Monad 进行有条件的更改。
假设,我在 State Monad 上有一个堆栈。
import Control.Monad.State
push :: Int -> State [Int] ()
push x = state $ \xs -> ((), x : xs)
pop :: State [Int] Int
pop = state $ \(x : xs) -> (x, xs)
然后,我想写一个函数来改变它。
someFunc :: Bool -> Int -> State [Int] ()
someFunc b x = do
let someValue = x * x
if b then
p <- pop
someValue = someValue + p
push $ someValue
例如,我想取一个布尔值b
和一个值x
。如果 b
是 True
(例如,这可能是我的堆栈不为空的条件),我想 pop
我的堆栈中的一个值并将其添加到某个变量。否则,我不向变量添加任何内容,只是将其压入堆栈。
我怎样才能实现这种行为?
这是最小的修复:
someFunc :: Bool -> Int -> State [Int] ()
someFunc b x = do
let someValue = x * x
if b then
do { p <- pop
; push $ someValue + p }
else
push $ someValue
if
表达式的两个分支必须具有相同的类型,并且在 do
符号内部必须是属于与整体 do
堵塞;特别是整个 do
块的类型,如果 if
表达式是其中的最后一个。
另请参阅:
do
notation tag info
do
notation explained, in vivid colors
在 Haskell 中,每个 if
都需要一个 else
,因为一切都只是一个值。此外,您不能执行 if ... then p <- pop ...
,因为 do
符号在 if
语句中丢失,因此您需要使用 if ... then do p <- pop ...
.
重新启动它
import Control.Monad.State
push :: a -> State [a] ()
push x = state $ \xs -> ((), x : xs)
pop :: State [a] a
pop = state $ \(x : xs) -> (x, xs)
someFunc :: (Num a) => Bool -> a -> State [a] ()
someFunc b x = do
let baseValue = x * x
someValue <- if b then do
p <- pop
return $ baseValue + p
else do
return baseValue
push $ someValue
除了您提出的问题之外,如何在没有 else
子句的情况下有条件地执行单子操作,您的代码还有另一个问题。您想在条件子句中重新分配一个 let 变量。
其他答案已经解决了这一点,并提供了正确的代码版本。我会在这里详细说明。所有 let 变量都是不可变的,包括内部 monad。您 可以 重新定义 monad 中的变量,但这只会隐藏原始变量,使其远离 same monadic 操作的代码。 someValue
的重新分配发生在嵌套的 monadic 操作中(有一个新的 do
块)并且在 if
.
之外不可见
有条件地重新分配 let 变量基本上是不可能的。编译器需要在编译时知道引用了哪个值。做到这一点的方法是 return
来自条件的值然后分配变量,如@Aplet123 的答案,或者 运行 条件的两个分支中的动作对不同的值,如@Will Ness说。
回答所述问题:您可以使用 Control.Monad
中的 when
函数有条件地 运行 单子动作(没有 else
分支)。所以如果你的代码真的没有else
分支,你可以这样写:
someFunc :: Bool -> Int -> State [Int] ()
someFunc b x = do
let someValue = x * x
when b $ do
p <- pop
push $ someValue + p
当然这对纯值没有意义,因为必须始终提供一个值。但是对于有条件执行的 monadic 动作(产生 ()
,或者我们有同样的问题:在条件为假的情况下 return 值是多少?)确实有意义。
出于好奇,when
定义如下:
when :: (Applicative f) => Bool -> f () -> f ()
when p s = if p then s else pure ()
所以 pure ()
作为 'null' 单子动作。
Haskelling 快乐!
我不知道如何在 Haskell 中对 State Monad 进行有条件的更改。 假设,我在 State Monad 上有一个堆栈。
import Control.Monad.State
push :: Int -> State [Int] ()
push x = state $ \xs -> ((), x : xs)
pop :: State [Int] Int
pop = state $ \(x : xs) -> (x, xs)
然后,我想写一个函数来改变它。
someFunc :: Bool -> Int -> State [Int] ()
someFunc b x = do
let someValue = x * x
if b then
p <- pop
someValue = someValue + p
push $ someValue
例如,我想取一个布尔值b
和一个值x
。如果 b
是 True
(例如,这可能是我的堆栈不为空的条件),我想 pop
我的堆栈中的一个值并将其添加到某个变量。否则,我不向变量添加任何内容,只是将其压入堆栈。
我怎样才能实现这种行为?
这是最小的修复:
someFunc :: Bool -> Int -> State [Int] ()
someFunc b x = do
let someValue = x * x
if b then
do { p <- pop
; push $ someValue + p }
else
push $ someValue
if
表达式的两个分支必须具有相同的类型,并且在 do
符号内部必须是属于与整体 do
堵塞;特别是整个 do
块的类型,如果 if
表达式是其中的最后一个。
另请参阅:
do
notation tag infodo
notation explained, in vivid colors
在 Haskell 中,每个 if
都需要一个 else
,因为一切都只是一个值。此外,您不能执行 if ... then p <- pop ...
,因为 do
符号在 if
语句中丢失,因此您需要使用 if ... then do p <- pop ...
.
import Control.Monad.State
push :: a -> State [a] ()
push x = state $ \xs -> ((), x : xs)
pop :: State [a] a
pop = state $ \(x : xs) -> (x, xs)
someFunc :: (Num a) => Bool -> a -> State [a] ()
someFunc b x = do
let baseValue = x * x
someValue <- if b then do
p <- pop
return $ baseValue + p
else do
return baseValue
push $ someValue
除了您提出的问题之外,如何在没有 else
子句的情况下有条件地执行单子操作,您的代码还有另一个问题。您想在条件子句中重新分配一个 let 变量。
其他答案已经解决了这一点,并提供了正确的代码版本。我会在这里详细说明。所有 let 变量都是不可变的,包括内部 monad。您 可以 重新定义 monad 中的变量,但这只会隐藏原始变量,使其远离 same monadic 操作的代码。 someValue
的重新分配发生在嵌套的 monadic 操作中(有一个新的 do
块)并且在 if
.
有条件地重新分配 let 变量基本上是不可能的。编译器需要在编译时知道引用了哪个值。做到这一点的方法是 return
来自条件的值然后分配变量,如@Aplet123 的答案,或者 运行 条件的两个分支中的动作对不同的值,如@Will Ness说。
回答所述问题:您可以使用 Control.Monad
中的 when
函数有条件地 运行 单子动作(没有 else
分支)。所以如果你的代码真的没有else
分支,你可以这样写:
someFunc :: Bool -> Int -> State [Int] ()
someFunc b x = do
let someValue = x * x
when b $ do
p <- pop
push $ someValue + p
当然这对纯值没有意义,因为必须始终提供一个值。但是对于有条件执行的 monadic 动作(产生 ()
,或者我们有同样的问题:在条件为假的情况下 return 值是多少?)确实有意义。
出于好奇,when
定义如下:
when :: (Applicative f) => Bool -> f () -> f ()
when p s = if p then s else pure ()
所以 pure ()
作为 'null' 单子动作。
Haskelling 快乐!