Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 理解差异列表的概念_Haskell_Optimization_Difference Lists - Fatal编程技术网

Haskell 理解差异列表的概念

Haskell 理解差异列表的概念,haskell,optimization,difference-lists,Haskell,Optimization,Difference Lists,当我阅读《真实世界哈斯克尔》的第13章时,我想到了差异列表的概念 作者说,在命令式语言中,如果我们想将一个元素附加到一个列表中,代价将是O(1),因为我们会保留一个指向最后一个元素的指针。 但是在Haskell中,我们有不可变的对象,因此每次都需要遍历列表并在末尾追加元素,因此0(n) 而不是[1,2,3]++[4]+[5] 我们可以使用部分应用程序:(++4)。(++5)[1,2,3] 我不明白这是如何提高效率的,因为: -当我做(++[5])[1,2,3]时,它仍然是O(n),然后是(+++

当我阅读《真实世界哈斯克尔》的第13章时,我想到了差异列表的概念 作者说,在命令式语言中,如果我们想将一个元素附加到一个列表中,代价将是
O(1)
,因为我们会保留一个指向最后一个元素的指针。 但是在Haskell中,我们有
不可变的
对象,因此每次都需要遍历列表并在末尾追加元素,因此
0(n)

而不是
[1,2,3]++[4]+[5]

我们可以使用部分应用程序:
(++4)。(++5)[1,2,3]


我不明白这是如何提高效率的,因为:
-当我做
(++[5])[1,2,3]
时,它仍然是
O(n)
,然后是
(+++4)
0(n)

引用

There are two very interesting things about this approach. The first is that the cost of a partial application is constant, so the cost of many partial applications is linear. The second is that when we finally provide a [] value to
unlock the final list from its chain of partial applications, application 
proceeds from right to left. This keeps the left operand of (++) small, and so 
the overall cost of all of these appends is linear, not quadratic

我知道这种方法非常迫切,因此,我们没有像作者所说的那样保留
尚未应用的方法的thunk,而是保留左操作数
小,但是。。。。我们仍然对每个附加执行遍历

给定一个列表:
[1…100]
并且想要追加
1,2
我仍然会遍历它
2次,因为我会:

  • f(f([1..100])=f([1..100,1])
    -遍历1次并追加
    1

  • [1..100,1,2]
    -第二次遍历以追加
    2

  • 有人能告诉我这在时间复杂度方面如何更有效吗?(因为
    space
    ——复杂性不再是因为
    thunks
    ,比如
    foldl'


    p.S

    我知道这个标准答案,我也读过,我觉得很有帮助。
    我知道你可以通过使用
    ,在左边附加
    +
    来实现
    O(1)
    复杂性,但它与
    +
    不同

    如果我在
    a=[1,2,3]
    上使用
    f=(:)


    (f4.f5)$a

    我可以说我每次追加都有
    0(1)
    效率,因为我总是在左边追加,但我不会得到
    [1,2,3,4,5]
    ,我会得到
    [5,4,1,2,3]
    ,那么,在这种情况下,
    差异列表
    对于附加一个元素的单一操作如何更有效呢?

    您需要更加注意时间,即当这件事或那件事发生时

    我们从不同的列表开始,而不是从列表开始

    f1 = ([1,2,3] ++)
    
    然后,在不断增长的差异列表的末尾“添加”4、5,我们有

    f2 = f1 . ([4] ++)
    f3 = f2 . ([5] ++)
    
    在增长差异列表末尾的每一个这样的添加都是O(1)

    当我们最终完成构建它时,我们将其按应用程序转换为“普通”列表

    xs = f3 []    -- or f3 [6..10] or whatever
    
    然后,我们小心地

    xs = ((([1,2,3] ++) . ([4] ++)) . ([5] ++)) []
       =  (([1,2,3] ++) . ([4] ++)) ( ([5] ++)  [] )
       =   ([1,2,3] ++) ( ([4] ++)  ( ([5] ++)  [] ))
       =     1:2:3:     (   4  :    (   5  :    [] ))
    
    根据
    (++)
    的定义

    一个典型的答案:


    即使是
    a1=(++[4])[1..
    本身也是一个O(1)操作,就像
    a2=(++[5])a1
    a3=(++[6])a2
    一样,因为Haskell是懒惰的,thunk创建是O(1)

    只有当我们访问最终结果时,整个操作才会变成二次的,因为访问
    ++
    结构不会重新排列它——它保持左嵌套状态,因此在从顶部重复访问时是二次的

    通过将左嵌套的
    结构转换为
    []
    结构,将该结构内部重新排列为右嵌套的
    $
    结构,如规范答案中所述,因此从顶部重复访问该结构是线性的

    因此,
    (++[5])(++[4])[1,2,3]
    (坏)和
    (([1,2,3]++)([4]++)([5]++)[]
    (好)之间存在差异。构建函数链本身是线性的,是的,但是它创建了一个完全访问的二次结构


    但是
    (([1,2,3]++)([5]++)([4]++)[]
    变成了
    ([1,2,3]++)([5]++)([4]++)([4]++)[])

    通常请参见。在差异列表中只允许
    (x:)
    (xs++)
    。(注意,参数在左边。)差异列表是有效的,因为它们将每个操作都强制到前置。那么
    差异列表
    foldl'
    之间的区别是什么?在每次迭代时,
    foldl'
    是否会将新值附加到
    缓冲区
    ,从而使我们免于
    thunks
    ?请参阅关于foldl的内容(免责声明:答案由我提供)
    foldl'(++)
    构建了一条左倾的
    (([1]++[2]++[3]++[4]++[5]
    。对于你的新问题,
    (f4.f5)$a=(:4)。(:5))[1,2,3]=4:5:[1,2,3]
    。如果你的意思是
    (++[4])(++[5])[1,2,3]=(++[4])([1,2,3]++[5])=([1,2,3]++[5])++[4]
    ,那么它再次构建了一个左倾链,即不好(二次型)。瞧,到底什么是不清楚的?