我很难理解本教程:https://acm.wustl.edu/functional/state-monad.php
我正在创建自己的函数来反转列表并返回一个State
,其中包含最低元素和列表的反向。我也是Haskell的新手。这是我的代码:
myFunct :: Ord a => [a] -> State a [a]
myFunct t = do
let s = reverse t
let a = minimum t
return s a
我也找不到其他材料。这是我得到的错误。
Couldn't match type ‘[a]’
with ‘StateT a Data.Functor.Identity.Identity [a]’
Expected type: a -> State a [a]
Actual type: a -> [a]
• The function ‘return’ is applied to two arguments,
its type is ‘a0 -> m0 a0’,
it is specialized to ‘[a] -> a -> [a]’
In a stmt of a 'do' block: return s a
In the expression:
do let s = reverse t
let a = minimum t
return s a
答案 0 :(得分:2)
由于您使用了do
阻止,我认为您希望将State
用作Monad
。那没关系,但我建议,将值列表([a]
)设为州,将单个最小值列为“返回值”。
这意味着您可以将函数类型简化为myFunct :: Ord a => State [a] a
。 [a]
是状态的类型,a
是返回值的类型。
请注意,没有明确的输入值'。在State
monad中,状态是一个隐含的上下文,总是在那里。
您现在可以像这样重写计算:
myFunct :: Ord a => State [a] a
myFunct = do
t <- get
let s = reverse t
put s
let a = minimum t
return a
你可以更简洁地编写计算,但我选择明确地编写它以使它更清楚地发生了什么。 get
检索隐式状态的当前值,put
覆盖状态。有关详细信息,请参阅the documentation。
您可以像这样运行:
*Q49164810> runState myFunct [42, 1337]
(42,[1337,42])
*Q49164810> runState myFunct [42, 1337, 0]
(0,[0,1337,42])
*Q49164810> evalState myFunct [42, 1337, 0]
0
*Q49164810> execState myFunct [42, 1337, 0]
[0,1337,42]
runState
采用初始状态,运行myFunct
计算,并返回返回值和最终状态。 evalState
的工作方式相同,但只返回返回值,而exacState
只返回最终状态。
答案 1 :(得分:2)
你很幸运:State
是最容易理解的单身。
在您使用标准库中的State
和reverse
的情况下,您的功能根本不需要minimum
,请不要气馁。
myFunct' :: Ord a => [a] -> ([a], a)
myFunct' xs = (reverse xs, minimum xs)
(它会像这样运行:)。
λ myFunct' [1,2,3]
([3,2,1],1)
请注意,为了将reverse
和minimum
同时应用于列表,您需要遍历两次。这是State
可能派上用场的时候:使用它,你只能遍历列表一次,因此,希望能获得一些加速。请继续阅读以了解具体方法。
所以, State
是一种特殊类型的函数:你给它的东西(也称为“状态”)保存在一个魔术盒中,你可以观察它,或者随时用另一种相同类型的东西替换它。如果您有使用命令式语言的经验,您可能会发现将State
视为命令式过程并将“状态”视为局部变量很容易。让我们回顾一下您可以用来构建和执行State
:
您可以使用(不恰当命名的)函数get
观察框中的内容。请注意,这不会以任何方式更改状态 - 您获得的仅仅是其当前值的不可变副本;东西留在盒子里。
您通常会将您的观察与名称相关联,然后将其用作普通值 - 例如,传递给纯函数:
stateExample1 :: State Integer Integer
stateExample1 = do
x <- get -- This is where we observe state and associate it with the name "x".
return $ x * 2 -- (* 2) is an example of a pure function.
λ runState stateExample1 10
(20,10) -- The first is the return value, the second is the (unchanged) state.
您可以替换框中的东西与其他适当类型的东西;使用函数put
:
stateExample2 :: State Integer Integer
stateExample2 = do
x <- get
put $ x * 2 -- You may think of it as though it were "x = x * 2"
-- in an imperative language.
return x
λ runState stateExample2 10
(10,20) -- Now we have changed the state, and return its initial value for reference.
请注意,虽然我们改变了状态,但我们对它的观察(我们命名为“x”)仍具有相同的值。
你可以运行 State
函数,给它一个参数(我们称之为“初始状态”):
y = runState stateExample1 10
与以下内容相同:
y = stateExample1(10);
- 使用类似C语法的命令式语言,除了您同时获得返回值和最终状态。
有了这些知识,我们现在可以像这样重写你提议的myFunct
:
myFunct :: Ord a => [a] -> State (Maybe a) [a]
myFunct [ ] = return [ ]
myFunct t = do
let s = reverse t
let a = minimum t
put (Just a)
return s
λ runState (myFunct [1,2,3]) (Just (-100))
([3,2,1],Just 1)
λ runState (myFunct []) (Just (-100))
([],Just (-100))
如果我们将State
视为一个命令性过程,那么反向列表就是它返回的内容,而列表的最小值则是它的最终状态。由于列表可能为空,我们已为最小值配置了可选的默认值。这使得函数 total ,这被认为是很好的Haskell风格:
λ myFunct' []
([],*** Exception: Prelude.minimum: empty list
λ runState (myFunct []) Nothing
([],Nothing)
现在,让我们通过编写一个函数来获得State
的好处,该函数在一次传递中返回列表的最小值和反向值:
reverseAndMinimum :: Ord a => [a] -> ([a], Maybe a)
reverseAndMinimum xs = runState (reverseAndMinimum' xs [ ]) Nothing
reverseAndMinimum' :: Ord a => [a] -> [a] -> State (Maybe a) [a]
reverseAndMinimum' [ ] res = return res
reverseAndMinimum' (x:xs) res = do
smallestSoFar <- get
case smallestSoFar of
Nothing -> put $ Just x
Just y -> when (x < y) (put $ Just x)
reverseAndMinimum' xs (x: res)
首先,这是一个迭代算法,因此需要一个最小值的起始值。我们在reverseAndMinimum'
隐藏了这一事实,为Nothing
提供了起始值。
我从现代Prelude.reverse
借来的反面部分的逻辑。我们只是将元素从第一个参数xs
移动到第二个参数res
,直到xs
为空。
这是找到当前x
中较小者和状态框中存储的值的部分。我希望你会发现它可读。
case smallestSoFar of
Nothing -> put $ Just x
Just y -> when (x < y) (put $ Just x)
这是进行递归的部分:
reverseAndMinimum' xs (x: res)
它再次应用reverseAndMinimum'
,但是应用于严格较小的列表xs
; monadic接线自动将当前最小值的盒子转移到线下。
让我们跟踪对reverseAndMinimum'
的调用的执行情况。假设我们说:
runState (reverseAndMinimum' [1,2,3] [ ]) Nothing
会发生什么?
1
和Nothing
中的较小者为1
。因此,框中的Nothing
将替换为Just 1
。 State
将再次被调用,就像我们用这样的代码调用它一样:
runState (reverseAndMinimum' [2,3] [1]) (Just 1)
依此类推,直到参数变为空列表,此时该框肯定会包含最小的数字。
此版本实际上更快 1>} 22%,并且使用的内存也更少。 (虽然,你可以查看编辑历史记录,但需要付出一些努力才能实现。)
就是这样。我希望它有所帮助!
特别感谢Li-Yao Xia helped me设计了myFunct'
实际击败reverseAndMinimum
的代码。