【问题标题】:using reverse on a self-defined list in Haskell在 Haskell 中的自定义列表上使用 reverse
【发布时间】:2014-04-24 23:38:02
【问题描述】:

我做了一个名为 Queue 的自定义数据类型,并尝试将 Integers 排入“列表”末尾。

data Queue = Ele Int Queue | Nil

-- [..] some other Functions

enqueue :: Int -> Queue -> Queue
enqueue x Nil = (Ele x Nil)
enqueue x (Ele _ restEles) = reverse (Ele x reverse restEles)

我得到错误:

“无法匹配预期类型Queue' with actual type[a0]'”。

我认为是这样,因为该函数不知道如何处理我的数据类型。我在这里吗?我怎样才能解决这个问题?我必须编写自己的反向函数吗?如果你能帮我解决这个问题,因为我还在学习 Haskell 并且不明白这一点:

reverse = foldl (flip (:)) []

【问题讨论】:

    标签: haskell functional-programming


    【解决方案1】:

    是的,您已经定义了一个新的数据类型。尽管它在结构上与列表相似,但 Haskell 会将其视为完全不同的。因此,您确实必须定义一个新的reverse 函数,或者定义一个将您的类型转换为标准列表并返回的函数。

    但对于enqueue,您不一定需要reverse。在 Ele 的情况下,您可以在 restEles 上递归调用 enqueue,并将第一个元素放回结果前面。

    这里有一个直接递归定义的模式供您完成:

    enqueue :: Int -> Queue -> Queue
    enqueue x Nil                     = Ele x Nil
    enqueue x (Ele firstEle restEles) = ...
    

    对于...,您必须按照我上面所说的进行:在其余部分递归调用enqueue 元素并将第一个元素放回顶部。

    【讨论】:

    • 谢谢!我在大学学习 Haskell,练习希望它具有运行时效率,并建议对 O(n) 使用反向。所以递归地我还需要告诉队列的最后一个元素,我猜它是跟随者。就像在 C 中设置 listpointers 一样。
    • 所以到目前为止我得到了这个:enqueue :: Int -> Queue -> Queue enqueue x Nil = (Ele x Nil) enqueue x (Ele _ Nil) = --TELL QUEUE THAT NEXT ELE IS NOT NIL enqueue x (Ele _ restEles) = enqueue x restEles< 问题是我在评论行中写的。
    • 好的,我添加了另一个提示。您只需要两个案例,而不是三个。 (顺便说一句,如果您使用 reverse 方法,您只需要 一个 案例,那么单独的 Nil 案例是多余的。)
    • True.. 第一种情况就是不要浪费时间。因为当没有队列时,我不需要递归它 2 次.. 无论如何我什至不知道它是否节省时间.. 可能只是你说的冗余代码.. 你的代码的问题是它使用 (Ele firstEle restEle),但 Queue 被定义为 (Ele Int Queue).. 还是我还没有得到它?
    • 哦,最后一次......对不起!我完全误读了这个练习。他们希望我们使用 Haskell 列表对其进行编程,因此使用 type Queue = [Int],然后使用反向也没问题。无论如何感谢您的帮助,我想我学到了一些新东西!
    【解决方案2】:

    反转的类型是:

    reverse :: [a] -> [a]
    

    队列不是[a]。您需要实现自己的 reverseQueue 函数来理解您的数据类型。

    【讨论】:

      【解决方案3】:

      关于你问题的第二部分:

      reverse :: [a] -> [a]
      reverse = foldl (flip (:)) []
      

      首先,确保您了解foldl。这是一个强大的高阶函数,这意味着它可以用来实现很多其他函数(sum,map,filter,当然还有reverse)。你可以阅读它here

      现在,我们来看一个更简单的版本:

      reverse :: [a] -> [a]
      reverse xs = foldl (\ys y -> y:ys) [] xs
      

      \ys y -> y:ys 是一个非常简单的函数:它接受一个列表 (ys) 和一个值 (y) 并将值插入到列表之前 (y:ys)。所以我们的计划是:从空列表([])开始,将xs中的第一项插入其左侧,取出结果并将第二项插入其左侧,依此类推。

      让我们用一个简单的列表来模拟它 - [1,2,3]:

      1. 我们从空列表开始 - []

      2. 将第一项 (1) 添加到左侧:[1]

      3. [1]左侧添加第二项(2):[2,1]

      4. [2,1]左侧添加第三项(3):[3,2,1]

      我们已经成功逆转[1,2,3]

      现在,flip 是一个函数,它接受一个函数并“翻转”它的参数。所以如果subtract a ba-b,那么(flip subtract) a b 等于subtract b a - b-a。因此,如果 (:) 是一个函数,它接受一个项目 y 和一个列表 ys 并将项目添加到列表的开头,那么 flip (:) 是具有翻转参数的相同函数 - 它接受一个列表和一个item,很像我们的函数 - \ys y -> y:ys。所以我们可以替换他们两个:

      reverse :: [a] -> [a]
      reverse xs = foldl (flip (:)) [] xs
      

      现在我们写成pointfree风格,并从等式两边消除xs,得到最终版本:

      reverse :: [a] -> [a]
      reverse = foldl (flip (:)) []
      

      【讨论】:

      • 谢谢.. 我想我知道 fold 是如何工作的,但是现在对我来说其余的更清楚了.. 至少这个foldl (\ys y -> y:ys) [] xs 不是那么难(我只是不知道 \ 功能)我很难理解翻转如何在没有参数的情况下“翻转”一些东西,但是一个函数 (:)..
      • ` is not a function - its marks a [lambda expression](http://www.haskell.org/haskellwiki/Anonymous_function). As for flip` - 尝试在 ghci 中使用它。创建一个有两个参数的函数,看看翻转它时会发生什么。 flip 是这些 Haskell 函数之一,它非常简单但非常令人困惑。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-20
      • 1970-01-01
      • 2021-10-09
      相关资源
      最近更新 更多