Haskell状态作为函数类型 [英] Haskell State as function type
问题描述
我很难理解本教程: https://acm.wustl .edu / functional / state-monad.php
我创建了自己的函数来反转列表并返回一个 State
与最低的元素和列表的反向。我对Haskell也很新。这是我的代码:
myFunct :: Ord a => [a] - >状态a [a]
myFunct t = do
让s =反转t
让a =最小值t
返回sa
我也找不到其他材料。这是我得到的错误。
无法将类型'[a]'
与'StateT a Data.Functor.Identity.Identity [a]'
预期类型:a - >州a [a]
实际类型:a - > [a]
•函数'return'应用于两个参数,
其类型为'a0 - > m0 a0',
它专用于'[a] - > a - > [a]'
在'do'块的标记中:return sa
在表达式中:
让s = reverse t
let a = minimum t
返回sa
您好运: State
是最容易理解的单子。
请不要因为函数不需要 State
,只要您使用 reverse
和 minimum
标准库。
myFunct':: Ord a => [a] - > ([a],a)
myFunct'xs =(reverse xs,minimum xs)
(它会像这样运行):
$ p $ λmyFunct'[1,2,3]
([3 ,2,1],1)
请注意,为了让您同时应用 reverse
和 minimum
添加到列表中,您需要遍历它两次。这就是 State
可能会得心应手的原因:使用它,你只能遍历一次列表,因此希望获得一些加速。
因此, State
是一个函数特殊类型:你给它的东西(也称为状态)被保存在一个魔术盒中,你可以观察它,或者随时用另一种相同类型的东西替换它。如果您有使用命令式语言的经验,您可能会发现将 State
视为命令式过程并将state视为局部变量很容易。让我们回顾一下你可能用来构造和执行 State
的工具:
-
您可以使用(不正确命名)函数
get
来观察框中的内容。请注意,这不会以任何方式更改状态 - 您获得的仅仅是其当前值的不可变副本;这个东西留在箱子里。
你通常会将你的观察与一个名称联系起来,然后用它作为一个普通的值 - 例如传递给一个纯函数: / p>
stateExample1 :: State Integer整数
stateExample1 = do
x< - get - 这是我们观察状态并将其与名称x相关联。
return $ x * 2 - (* 2)是纯函数的一个例子。
λrunState stateExample1 10
(20,10) - 第一个是返回值,第二个是(不变)状态。
-
您可以 打字的东西;使用函数
put
:stateExample2 :: State Integer Integer
stateExample2 = do
x< - get
put $ x * 2 - 您可以将它当作是x = x * 2
- 以命令式语言。
return x
λrunState stateExample2 10
(10,20) - 现在我们改变了状态,并返回其初始值作为参考。
请注意,尽管我们改变了状态,但我们对它的观察(我们称之为x)您仍然可以运行
状态
功能。 ,给它一个参数(我们称它为 initial state ):
y = runState stateExample1 10
它与
y = stateExample1(10);
- 使用类C语法的命令式语言,除了您获得返回值和 。
有了这些知识,我们现在可以重写您提出的 myFunct
就像这样:
myFunct :: Ord a => [a] - > State(可能a)[a]
myFunct [] = return []
myFunct t = do
let s = reverse t
let a = minimum t
put (只是)
返回s
$ b($ 100)
([3,2,1],只有1)b $ b
λrunState(myFunct [1,2,3])
runState(myFunct [])(Just(-100))
([],Just(-100))
如果我们将 State
作为命令过程,那么反向列表就是它返回的内容,而列表的最小值是它的最终状态是。由于列表可能为空,因此我们为最小值设置了可选的默认值。这使得函数 total ,这被认为是很好的Haskell风格:
λmyFunct'[]
([],***例外:Prelude.minimum:空列表
λrunState(myFunct [])Nothing
([],Nothing)
现在,让我们收获状态
通过编写一个函数返回列表的最小值和最后一个值:
reverseAndMinimum :: Ord a => [a] - >([a],也许a)
reverseAndMinimum xs = runState(reverseAndMinimum'xs [])Nothing
reverseAndMinimum': :Ord a => [a] - > [a] - >状态(也许a)[a]
reverseAndMinimum'[] res = return res
reverseAndMinimum'(x:xs)res = do
smallestSoFar< - 获得
case $ small $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $只需要 - >当(x reverseAndMinimum'xs(x:res)
-
首先,这是一个迭代算法,因此需要一个起始值。我们将这个事实隐藏在
reverseAndMinimum'
中,为初始值提供Nothing
。 -
我从现代的
Prelude.reverse
。我们只是将元素从第一个参数xs
移动到第二个参数res
,直到xs
为空。 这是找到当前 -
这是递归:
reverseAndMinimum'xs(x:res)
它再次应用
reverseAndMinimum'
,但应用于一个严格小的列表xs
;一元接线自动将箱子与当前线路的最小值一起转移。 我们来追踪一次通话的执行情况到 -
1
和没有
是1
。因此,框中的Nothing
将被替换为Just 1
。 -
State
x $ c $中较小者的部分c>和存储在状态框中的值。我希望你会发现它可读。 case $
的情况minimSoFar Nothing - >把$只是x
只是y - > when(x
reverseAndMinimum'
。假设我们说:
runState(reverseAndMinimum'[1,2,3] [])Nothing
会发生什么?
会再次被调用,就像我们用这样的代码调用它一样: <$ p $ b $
runState(reverseAndMinimum'[2,3] [1])(just 1)
等等,直到参数成为一个空列表,此时该框肯定会包含最小的数字。
此版本实际上比 的 而且使用的内存也少一些。 (虽然,您可能会在编辑历史记录中查看,但需要付出一些努力才能实现。)
就是这样。我希望它有帮助!
特别感谢帮助我 a>设计 reverseAndMinimum
的代码,它实际上击败 myFunct'
。
I am having a hard time understanding this tutorial: https://acm.wustl.edu/functional/state-monad.php
I am creating my own function that reverses a list and returns a State
with the lowest element and the reverse of the list. I am very new to Haskell as well. Here is my code:
myFunct :: Ord a => [a] -> State a [a]
myFunct t = do
let s = reverse t
let a = minimum t
return s a
I can't find an other material on this either. This is the error I am getting.
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
You're in luck: State
is the easiest monad to understand.
Please do not get discouraged by the fact that your function does not need State
at all, insofar as you use reverse
and minimum
from the standard library.
myFunct' :: Ord a => [a] -> ([a], a)
myFunct' xs = (reverse xs, minimum xs)
(It would run like this:)
λ myFunct' [1,2,3]
([3,2,1],1)
Notice though that, in order for you to apply both reverse
and minimum
to a list, you will need to traverse it two times. This is when State
may get handy: using it, you can only traverse the list once, thus, hopefully, gaining some speedup. Read on to find out how.
So, State
is a function of a special kind: the thing you give it (also called "state") is kept in a magic box where you can observe it, or replace it with another thing of the same type at any time. If you have experience with imperative languages, you may find it easy to think of State
as an imperative procedure and "state" as a local variable. Let us review the tools that you may use to construct and execute a State
:
You may observe the thing in the box with the (inappropriately named) function
get
. Notice that this does not change the state in any way − what you obtain is merely an immutable copy of its current value; the thing stays in the box.You would usually associate your observation with a name, then use it as an ordinary value − for example, pass to a pure function:
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.
You may replace the thing in the box with another suitably typed thing; use the function
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.
Notice that, though we changed the state, our observation of it (that we named "x") still has the same value.
You may run the
State
function, giving it an argument (we'd call it "initial state"):y = runState stateExample1 10
It is the same as:
y = stateExample1(10);
− in an imperative language with C-like syntax, except that you obtain both the return value and the final state.
Armed with this knowledge, we can now rewrite your proposed myFunct
like this:
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))
If we regard State
as an imperative procedure, then the reversed list is what it returns, while the minimum of the list is what its final state would be. As the list may be empty, we have provisioned an optional default value for the minimum. This makes the function total, which is considered good Haskell style:
λ myFunct' []
([],*** Exception: Prelude.minimum: empty list
λ runState (myFunct []) Nothing
([],Nothing)
Now, let us reap the benefit of State
by writing a function that returns both the minimum and the reverse of a list in one pass:
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)
First off, this is an iterative algorithm that thus needs a starting value for the minimum. We hide this fact in
reverseAndMinimum'
, supplyingNothing
for the starting value.The logic of the reverse part I borrowed from the modern
Prelude.reverse
. We simply move elements from the first argumentxs
to the second argumentres
, untilxs
is empty.This is the part that finds the smaller of the current
x
and the value stored in the state box. I hope you'll find it readable.case smallestSoFar of Nothing -> put $ Just x Just y -> when (x < y) (put $ Just x)
This is the part that does the recursion:
reverseAndMinimum' xs (x: res)
It applies
reverseAndMinimum'
again, but to a strictly smaller listxs
; the monadic wiring automagically transfers the box with the current minimum down the line.
Let us trace the execution of a call to reverseAndMinimum'
. Suppose we say:
runState (reverseAndMinimum' [1,2,3] [ ]) Nothing
What will happen?
- The smaller of
1
andNothing
is1
. So, theNothing
in the box will be replaced byJust 1
. The
State
will be called again, as though we called it with a code like this:runState (reverseAndMinimum' [2,3] [1]) (Just 1)
And so on, until the parameter becomes an empty list, by which time the box will surely contain the smallest number.
This version actually performs faster than myFunct'
by about 22%, and uses somewhat less memory as well. (Though, as you may check in edit history, it took some effort to get to it.)
That's it. I hope it helps!
Special thanks to Li-Yao Xia who helped me devise the code for reverseAndMinimum
that actually beats myFunct'
.
这篇关于Haskell状态作为函数类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!