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
List Haskell:处理死锁的自引用列表_List_Haskell_Recursion_Deadlock_Self Reference - Fatal编程技术网

List Haskell:处理死锁的自引用列表

List Haskell:处理死锁的自引用列表,list,haskell,recursion,deadlock,self-reference,List,Haskell,Recursion,Deadlock,Self Reference,GHC是否有任何有用的理由允许以下内容永久阻止: list = 1 : tail list 在列表迭代器/生成器中,似乎有点复杂,我们应该能够做一些更有用的事情: 返回错误“无限阻塞列表” 返回[1,1] 解释2:当进入生成器以获取元素N时,我们可能会在生成器内进行所有自引用,仅限于列表,但以N-1结尾(我们注意到范围内的读取N并返回列表结尾)。这是一种使用作用域的简单死锁检测 显然,这对于上面的玩具示例没有多大用处,但它可能允许更有用/优雅的有限、自引用列表定义,例如: primes = f

GHC是否有任何有用的理由允许以下内容永久阻止:

list = 1 : tail list
在列表迭代器/生成器中,似乎有点复杂,我们应该能够做一些更有用的事情:

  • 返回
    错误“无限阻塞列表”
  • 返回
    [1,1]
  • 解释2:当进入生成器以获取元素
    N
    时,我们可能会在生成器内进行所有自引用,仅限于列表,但以
    N-1
    结尾(我们注意到范围
    内的
    读取N
    并返回列表结尾)。这是一种使用作用域的简单死锁检测

    显然,这对于上面的玩具示例没有多大用处,但它可能允许更有用/优雅的有限、自引用列表定义,例如:

    primes = filter (\x -> none ((==0).mod x) primes) [2..]
    
    请注意,这两种更改都只会影响当前将导致无限块的列表生成器,因此它们看起来向后兼容语言更改

    暂时忽略进行这种更改所需的GHC复杂性,这种行为会破坏我所缺少的任何现有语言行为吗?关于这一变化的“优雅”还有其他想法吗

    另请参见下面的另一个BFS示例。对我来说,这似乎比其他一些解决方案更实用/优雅,因为我只需要定义bfsList是什么,而不是如何生成它(即指定终止条件):


    即使
    list
    永远在GHCi下循环,使用GHC编译的适当二进制文件也会检测到循环并发出错误信号。如果编译并运行:

    list = 1 : tail list
    main = print list
    
    它以错误消息终止:

    Loop: <<loop>>
    
    在Haskell语言中具有定义良好的语义。这些语义赋予它一个值,这个值是“底部”(或“错误”或符号
    \u124;
    ),就像
    head[1,2,3]
    的值是
    1
    一样

    (从技术上讲,
    list
    的值是
    1:| |
    ,几乎是“最底层”。这就是@Justin Li在他的评论中所说的。我试图在下面解释为什么它有这个值。)

    虽然您可能看不到返回bottom的程序或表达式的使用,也看不到在“向后兼容”的基础上为此类表达式指定非bottom语义的危害,但Haskell社区的大多数人(语言设计师、编译器开发人员和有经验的用户)都会不同意您的观点,因此,不要期望与他们一起取得多大进展

    至于你提出的具体的新语义,它们还不清楚。为什么
    列表的值不等于
    [1]
    ?在我看来,当我输入“生成器”以获取元素n=1(零索引,因此第二个元素)并计算
    尾部列表时,那么以元素n-1=0结尾的
    列表是
    [1]
    ,尾部等于
    []
    ,所以我认为应该得到以下结果,对吗

    list = 1 : tail list
         = 1 : tail [1]   -- use list-so-far
         = 1 : []
         = [1]
    
    为什么该值(几乎)处于底部 根据标准Haskell的语义,
    list
    的值(几乎)位于底部(但请参见末尾的注释)

    作为参考,
    tail
    的定义实际上是:

    tail l = case l of _:xs -> xs
                       [] -> error "ack, you dummy!"
    
    让我们尝试使用Haskell语义“完全”评估
    list

    -- evaluating `list` using definition of `list`
    list = 1 : tail list
    
    -- evaluating `tail list` using definition of `tail`
    list = 1 : case list of _:xs -> xs
                            ...
    -- evaluating case construct requires matching `list` to
    -- a pattern, this requires evaluation of `list` using its defn
    list = 1 : case (1 : tail list) of _:xs -> xs
                                       ...
    -- case pattern match succeeds
    list = 1 : let xs = tail list in xs    -- just to be clear
         = 1 : tail list
    
    -- awesome, now all we need to do is evaluate:
    list = 1 : tail list
    -- ummm, Houston, we have a problem
    
    最后的无限循环就是表达式“几乎是底部”的原因


    注意:实际上有几个不同的Haskell语义集,计算Haskell表达式值的不同方法。黄金标准是@luqui回答中描述的指称语义。我在上面使用的那些,充其量是Haskell报告中描述的“非正式语义”的一种形式,但它们足够好,可以得到正确的答案。

    下面是一个关于
    list=1:⊥

    首先,一点背景。在Haskell中,值按“定义”部分排序,其中值包含&bot;(“底部”)的定义比没有的定义少。所以

    • 的定义小于
      1:⊥
    • 1:⊥的定义少于
      1:2:3:[]
    但这是部分订单,所以

    • 1:⊥的定义不少于
      2:3:⊥,也没有更多的定义
      
    即使第二个列表更长<代码>1:⊥的定义仅比以1开头的列表少。我强烈推荐阅读哈斯克尔的著作

    现在谈谈你的问题。看

    list = 1 : tail list
    
    作为要求解的方程,而不是“函数声明”。我们这样重写它:

    list = ((1 :) . tail) list
    
    从这个角度来看,我们看到
    list
    是一个固定点

    其中
    f=(1:)。尾部
    。在Haskell语义中,递归值是通过根据上述顺序找到最小不动点来解决的

    找到这个的方法很简单。如果你从⊥, 然后反复应用这个函数,你会发现一个不断增加的价值链。链停止变化的点(从技术上讲,这是链的极限,因为它可能永远不会停止变化)

    从⊥,

    f ⊥ = ((1 :) . tail) ⊥ = 1 : tail ⊥
    
    我们看到了⊥ 已经不是一个固定点了,因为我们没有得到⊥ 从另一端出去。那么,让我们用我们得到的东西再试一次:

    f (1 : tail ⊥) = ((1 :) . tail) (1 : tail ⊥)
                   = 1 : tail (1 : tail ⊥)
                   = 1 : tail ⊥
    
    哦,看,这是一个固定点,我们得到的东西和我们放进去的一样

    这里重要的一点是,这是最起码的一个。您的解决方案
    [1,1]=1:1:[]
    也是一个固定点,因此它解决了以下等式:

    f (1:1:[]) = ((1 :) . tail) (1:1:[]) 
               = 1 : tail (1:1:[])
               = 1:1:[]
    
    但当然,每个以1开头的列表都是一个解决方案,我们不清楚应该如何在它们之间进行选择。然而,我们通过递归
    1找到的:⊥的定义比它们都少,它提供的信息不超过公式所需的信息,这是语言指定的信息。

    虽然这可能是simp
    f ⊥ = ((1 :) . tail) ⊥ = 1 : tail ⊥
    
    f (1 : tail ⊥) = ((1 :) . tail) (1 : tail ⊥)
                   = 1 : tail (1 : tail ⊥)
                   = 1 : tail ⊥
    
    f (1:1:[]) = ((1 :) . tail) (1:1:[]) 
               = 1 : tail (1:1:[])
               = 1:1:[]