【问题标题】:Adding an element to a list and then reverse it将元素添加到列表中,然后将其反转
【发布时间】:2018-11-15 16:21:43
【问题描述】:

我是 learning Haskell,正在尝试理解列表。

从研究到将元素添加到列表中,您通常会这样做:

let numbers = [4,8,15,16,23,42] 
numbers ++ [56]

但要引用this answer:

如果您需要这样做,通常有更好的方法来构建 你的算法。例如,您可以按相反的顺序构建您的list (在开头添加元素),最后只调用reverse

代码

let numbers = [23,43,56]
let newNumbers = 69:numbers
reverse newNumbers

输出

[56,43,23,69]

问题

  1. 根据引用的答案,我编写的代码是否正确?
  2. 我想更好地理解术语,我可以说我正在向listhead 添加元素吗?据我了解,每一个新元素都是第一个元素,当我写head newNumbers时返回的值。

【问题讨论】:

  • 这并不意味着“在你添加每个 itme 之后调用reverse”;这并不比使用++ 附加项目更好。相反,这意味着如果您想通过在末尾添加新项目来构建列表,则应将它们放在开头,并在需要消耗整个列表时调用 reverse。
  • @chepner - 所以在这种情况下,如果我只想将69 添加到列表中,我是否按照报价正确地完成了它?这也让我想知道,您如何将多个元素添加到列表的开头?猜猜这是另一个可以尝试的练习。
  • 这真的取决于你打算如何使用这个列表。例如,请参阅我的答案。
  • 有时您必须将一个列表附加到另一个列表,更糟糕的是,您可能需要将新列表附加到结果列表数千次。这将是一个非常昂贵的操作。为了克服这种成本差异,使用了列表数据结构。 This question is all about that
  • 这不是关于haskell的问题,而是关于stackoverflow问题的问题。添加标签使查找有关 haskell 的问题变得更加困难。这也不是关于“添加”的问题

标签: list haskell reverse


【解决方案1】:

您需要区分链表数据结构和您使用链表实现的任何类似列表的数据类型。你可以做两件事来修改链表:在链表中添加一个新的头,并删除当前的头(如果链表不为空)。

引用所谈到的用例对于队列数据类型很常见:您可以添加到一端并从另一端移除。您可以使用 两个 链接列表来实现这一点,方法是向一个列表添加新元素并从另一个列表中删除元素。队列的实现负责根据需要进行反转,以确保您在删除之前插入的所有其他项目之前永远不会删除项目。

data Queue a = Queue [a] [a]

-- Put new elements on the incoming list.
addToQueue :: a -> Queue a -> Queue a
addToQueue x (Queue incoming outgoing) = Queue (x:incoming) outgoing

-- Take elements from the outgoing list, whose elements are stored
-- in the reverse order that they were added to the incoming list
-- previously.
removeFromQueue :: Queue a -> (a, Queue a)
removeFromQueue (Queue [] []) = error "Cannot remove from empty queue"
removeFromQueue (Queue incoming (x:xs)) = (x, Queue incoming xs)
removeFromQueue (Queue incoming []) = removeFromQueue (Queue [] (reverse incoming))

(我们不关心处理从空队列中删除的好方法;只需将其称为错误并保留它。)

添加到传入列表和从传出列表中删除很容易。棘手的部分是我们如何以及何时将项目从传入列表传输到传出列表。我们只在传出列表为空时这样做,当它为空时,我们立即传输整个传入列表,并在此过程中将其反转。换句话说,我们正在反向构建传入列表, 但仅在必要时才将其反转,而不是在添加每个项目之后。

摊销分析可用于表明虽然reverse 可能很慢,但它可以通过之前和之后的快速操作数量来平衡。

【讨论】:

  • 眼尖的读者可能会注意到我遗漏了peek :: Queue a -> a 函数。当传出列表为空时,确保一系列 peek 操作不必重复扫描传入列表,这会使其他两个函数复杂化,因为我不想处理这个答案。解决方案是确保addToQueueremoveFromQueue 都不能返回传出列表为空的队列。
  • (除非队列本身是空的,即。)
  • that 队列的摊销分析仅在队列被临时使用时才成立。在持久化应用程序中,需要以不同的方式进行旋转。
猜你喜欢
  • 1970-01-01
  • 2015-08-13
  • 2017-01-16
  • 2017-06-03
  • 1970-01-01
  • 1970-01-01
  • 2014-06-16
相关资源
最近更新 更多