【问题标题】:Can you recognize an infinite list in a Haskell program? [duplicate]你能识别 Haskell 程序中的无限列表吗? [复制]
【发布时间】:2012-04-10 23:21:18
【问题描述】:

可能重复:
How to tell if a list is infinite?

在 Haskell 中,您可以定义一个无限列表,例如 [1..]。 Haskell 中是否有内置函数来识别列表是否具有有限长度?我不认为可以编写用户提供的函数来执行此操作,但 Haskell 的列表的内部表示可能能够支持它。如果不在标准 Haskell 中,是否有提供此类功能的扩展?

【问题讨论】:

  • 我不这么认为。由于惰性求值,Haskell 运行时真的不知道列表是否是无限的;并不是说它只是不将其暴露给程序。
  • 我强烈怀疑这相当于所谓的“停机问题”,在这种情况下,答案将是“否”。
  • 如果你希望能够识别无限的结构,你可以阅读一些关于 coduction 的文章:en.wikipedia.org/wiki/Coinduction, disi.unige.it/dottorato/corsi/DPCI2011
  • 这是一个永远不会给出错误答案的函数:isInfinite list = case list of [] -> False; _:rest -> isInfinite rest
  • 没有“内部表示”,因为列表和其他所有东西一样只是 ADT

标签: list haskell


【解决方案1】:

不,这是不可能的。编写这样的函数是不可能的,因为您可以拥有其有限性可能未知的列表:考虑一个递归循环,生成它可以找到的所有twin primes 的列表。或者,为了跟进 Daniel Pratt 在 cmets 中提到的内容,您可以列出 universal Turing machine 在执行期间采取的所有步骤,并在机器停止时结束该列表。然后,您可以简单地检查这样的列表是否无限,并解决Halting problem

实现可以回答的唯一问题是列表是否是循环的:如果它的尾指针之一指向列表的前一个单元格。但是,这是特定于实现的(Haskell 没有指定任何关于实现必须如何表示值的内容)、不纯的(编写相同列表的不同方式会给出不同的答案),甚至取决于您传入的列表是否这样的功能已经被评估过了。即便如此,在一般情况下,它仍然无法区分有限列表和无限列表!

(我提到这一点是因为,在许多语言中(例如 Lisp 家族的成员),循环列表是唯一一种无限列表;没有办法表达类似“一个所有整数”。因此,在这些语言中,您可以检查列表是否是有限的。)

【讨论】:

  • 要检查列表是否循环,请查看vacuum,它可以返回 Haskell 值的堆布局图,然后您可以检查循环。但是,如上所述,这既是 GHC 特有的,也是不纯的。
  • 不知道孪生素数的(无限)是不可判定的;到目前为止,回答这个问题真的、真的、真的、真的很难。
  • @jwodder:是的,措辞不好。我已经编辑了我的答案。
  • (删除了之前的评论,没有仔细阅读 Q)。实际上,任何体面的 Lisp 都完全有能力实现惰性不定列表,从而表达类似“所有整数的列表”之类的东西,在它们的尾部带有生成器表达式的有限列表。列表数据对象本身当然是有限的,但它会表达一个无限的列表抽象。这种情况与我想象的 Haskell 实现没有任何不同。
【解决方案2】:

除了迭代列表以在我知道的任何实现中搜索最终的[] 之外,没有任何方法可以测试列表的有限性。而且一般来说,如果不去寻找结尾,就不可能判断一个列表是有限的还是无限的(这当然意味着每次你得到一个答案,那就是有限的)。

【讨论】:

    【解决方案3】:

    您可以围绕 list 编写一个包装器类型来跟踪无限性,并将自己限制为仅“可判定”操作(有点类似于 NonEmpty,它避免了空列表):

    import Control.Applicative
    
    data List a = List (Maybe Int) [a]
    
    infiniteList (List Nothing _) = true
    infiniteList _ = false
    
    emptyList = List (Just 0) []
    singletonList x = List (Just 1) [x]
    cycleList xs = List Nothing (cycle xs)
    numbersFromList n = List Nothing [n..] 
    appendList (List sx xs)  (List sy ys) = List ((+) <$> sx <*> sy) (xs ++ ys)
    tailList (List s xs) = List (fmap pred s) (tail xs)
    ...
    

    【讨论】:

    • 实际存储长度有什么好处,不是一个简单的Bool 吗?毕竟,对于一个有限列表,它是多余的。
    • Bool 也可以,但基本上我们希望能够找出我们的列表“有多长”,所以为什么不利用附加参数,特别是因为 length 对于长名单?
    【解决方案4】:

    正如 ehird 所写,您唯一的希望是找出一个列表是否是循环的。这样做的一种方法是使用 Haskell 的扩展,称为“可观察共享”。例如:http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.31.4053

    【讨论】:

      【解决方案5】:

      当谈到“列表的内部表示”时,从 Haskell 实现的角度来看,没有无限列表。您询问的“列表”实际上是对计算过程的描述,而不是数据对象。计算机内部没有数据对象是无限的。这样的事情根本不存在。

      正如其他人告诉你的那样,内部列表数据可能是循环的,实现通常能够检测到这一点,具有指针相等的概念。但是 Haskell 本身并没有这样的概念。

      这是一个用于检测列表循环的 Common Lisp 函数。 cdr 在列表中前进一个等级,cddr - 前进两个等级。 eq 是一个指针相等谓词。

      (defun is-cyclical (p)
        (labels ((go (p q) 
                   (if (not (null q)) 
                     (if (eq p q) t 
                       (go (cdr p) (cddr q))))))
          (go p (cdr p))))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-29
        • 1970-01-01
        • 1970-01-01
        • 2017-09-28
        • 1970-01-01
        • 2021-02-14
        • 2021-12-01
        • 2018-01-29
        相关资源
        最近更新 更多