List 将元素添加到列表中,然后将其反转

List 将元素添加到列表中,然后将其反转,list,haskell,reverse,List,Haskell,Reverse,我是,并且试图理解列表 从研究到向列表中添加元素,您通常会执行以下操作: let numbers = [4,8,15,16,23,42] numbers ++ [56] 但请引述: 如果你需要这样做,通常有一个更好的方法来组织 你的算法。例如,您可以按相反顺序构建列表 (在开头添加元素)并仅在结尾调用reverse 代码: let numbers = [23,43,56] let newNumbers = 69:numbers reverse newNumbers [56,43,23,69

我是,并且试图理解列表

从研究到向列表中添加元素,您通常会执行以下操作:

let numbers = [4,8,15,16,23,42] 
numbers ++ [56]
但请引述:

如果你需要这样做,通常有一个更好的方法来组织 你的算法。例如,您可以按相反顺序构建
列表
(在开头添加元素)并仅在结尾调用
reverse

代码

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

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

let numbers = [23,43,56]
let newNumbers = 69:numbers
reverse newNumbers
[56,43,23,69]
  • 根据引用的答案,我写的代码正确吗
  • 我想更好地理解术语,我可以说我正在向
    列表的
    头部添加元素吗?根据我的理解,每个新元素都是第一个元素,当我编写
    headnewnumbers
    时返回的值

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

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

    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))
    
    (我们不关心如何处理从空队列中删除的问题;只需将其称为错误,并将其保留在此处。)

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


    摊销分析可以用来表明,尽管
    反向
    可能很慢,但它是由前面和后面的快速操作的数量来平衡的。

    这并不意味着“在每个itme之前调用
    反向
    ”;这并不比使用
    ++
    附加项目好多少。相反,这意味着如果你想通过在列表的末尾添加新的项目来建立一个列表,你应该将它们放在列表的开头,并在消耗整个列表时调用reverse。@chepner-那么,在这种情况下,如果我只想将
    69
    添加到列表中,我是否按照引号正确地完成了操作?这也让我想知道,如何在列表的开头添加多个元素?我猜这是另一个要尝试的练习。这实际上取决于你打算如何使用列表。请看我的答案中的一个例子。有时你必须明确地将一个列表附加到另一个列表中,更糟糕的是,你可能需要将新列表附加到结果列表中数千次。这将是一个非常昂贵的操作。为了克服这一成本差异,使用了列表数据结构。这不是关于haskell的问题,而是关于stackoverflow的问题。添加标签会使查找有关haskell的问题变得更加困难。这也不是关于“添加”的问题,目光敏锐的读者可能会注意到我遗漏了一个
    peek::Queue a->a
    函数。当传出列表为空时,确保一系列的
    peek
    操作不必重复扫描传入列表,这会使其他两个函数变得复杂,这是我不想为这个答案处理的。解决方案是确保无论是
    addToQueue
    还是
    removeFromQueue
    都不能返回传出列表为空的队列。(即,除非队列本身为空。)该队列的摊销分析仅在短暂使用队列时有效。在持久性应用程序中,旋转需要以不同的方式进行。