Haskell状态作为函数类型 [英] Haskell State as function type

查看:91
本文介绍了Haskell状态作为函数类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很难理解本教程: 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 为空。

  • 这是找到当前 x 和存储在状态框中的值。我希望你会发现它可读。

      case $ 
    的情况minimSoFar Nothing - >把$只是x
    只是y - > when(x


  • 这是递归:

      reverseAndMinimum'xs(x:res)

    它再次应用 reverseAndMinimum',但应用于一个严格小的列表 xs ;一元接线自动将箱子与当前线路的最小值一起转移。

  • 我们来追踪一次通话的执行情况到 reverseAndMinimum'。假设我们说:

      runState(reverseAndMinimum'[1,2,3] [])Nothing 

    会发生什么?


    1. 1 没有 1 。因此,框中的 Nothing 将被替换为 Just 1

    2. State 会再次被调用,就像我们用这样的代码调用它一样:

      <$ 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', supplying Nothing for the starting value.

    • The logic of the reverse part I borrowed from the modern Prelude.reverse. We simply move elements from the first argument xs to the second argument res, until xs 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 list xs; 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?

    1. The smaller of 1 and Nothing is 1. So, the Nothing in the box will be replaced by Just 1.
    2. 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屋!

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