如何判断列表是否无限?

ale*_*sch 41 haskell list infinite

有没有办法判断Haskell中的列表是否无限?原因是我不想将函数length应用于无限列表.

C. *_*ann 29

应用于length未知列表通常是一个坏主意,实际上是由于无限列表,并且在概念上是因为通常事实证明你实际上并不关心长度.

你在评论中说:

我对Haskell很新,所以现在,不是无限结构使我的程序非常脆弱吗?

并不是的.虽然我们中的一些人希望有更好的方法来区分必然有限和必然无限的数据,但是当您逐步创建,处理检查惰性结构时,您总是安全的.计算长度显然不是渐进的,但检查是否长度为高于或低于一定的临界值,很多时候这就是你想做的事!

一个简单的案例是测试非空列表.isNonEmpty xs == length xs > 0是一个糟糕的实现,因为它检查无限数量的元素,当检查一个元素就足够了!比较一下:

isNonEmpty [] = False
isNonEmpty (_:_) = True
Run Code Online (Sandbox Code Playgroud)

这不仅是安全的,适用于一个无限列表,它也更有限名单更有效-它在列表的长度只需要一定的时间,而不是时间的线性.它也是标准库函数null的实现方式.

为了对相对于截止值进行长度测试进行推广,您显然需要检查列表的大小与您要比较的长度.我们可以使用标准库函数完成此操作,而不是更多drop:

longerThan :: Int -> [a] -> Bool
longerThan n xs = isNonEmpty $ drop n xs
Run Code Online (Sandbox Code Playgroud)

给定一个长度n和一个(可能是无限的)列表xs,这会删除它们存在的第一个n元素xs,然后检查结果是否为非空.因为drop如果生产的空单n比列表的长度较大,这正常工作对所有正n(唉,有没有非负整数类型,例如自然数,在标准库).


这里的关键点是,在大多数情况下,将列表视为迭代流而不是简单的数据结构会更好.如果可能,您希望执行转换,累积,截断等操作,并生成另一个列表作为输出或仅检查已知有限量的列表,而不是尝试一次处理整个列表.

如果你使用这种方法,你的函数不仅可以在有限列表无限列表上正常工作,而且它们还可以从懒惰和GHC优化器中获益更多,并且可能运行得更快并且使用更少的内存.


小智 26

停机问题首先证明了假设停机甲骨文存在,然后写一个函数,做了什么,甲骨文说会发生相反的不可解.我们在这里重现:

isInfinite :: [a] -> Bool
isInfinite ls = {- Magic! -}
Run Code Online (Sandbox Code Playgroud)

现在,我们想要制作一个impossibleListisInfinite它应该做的相反的列表.所以,如果impossibleList是无限的,它实际上是[],如果它不是无限的,它就是something : impossibleList.

-- using a string here so you can watch it explode in ghci
impossibleList :: [String]
impossibleList =
    case isInfinite impossibleList of
        True -> []
        False -> "loop!" : impossibleList
Run Code Online (Sandbox Code Playgroud)

自己尝试用ghci isInfinite = const TrueisInfinite = const False.

  • 没有什么能像对角化论证那样永远毁掉一切.我责备康托尔开始整个事情. (17认同)
  • 难道你不能使用类似的"证明"来表明无法检查列表是否为非空?用`isNonEmpty`替换`isInfinite`.但是`isNonEmpty`显然可以实现. (10认同)
  • @CA麦肯:前提是`isInfinite`决定给定名单的无限大,即它返回TRUE;任何无限名单和`FALSE`任何有限列表...它做什么,如果你给它⊥是既非这里也非那里.我们只能传递一个有限的名单,并表明它_doesn't_返回`FALSE`推导出矛盾(计算结果为TRUE;或⊥),或者通过传递一个无限的列表,并表明它_doesn't_返回`真`(评估为'False`或⊥).给它⊥并显示它产生⊥只表明`isInfinite`是严格的. (7认同)
  • @CA McCann:这不仅没有说'isInfinite`的任何内容,它也没有提及可判定性或停止问题.考虑`f :: Int32-> Bool`形式的函数.由于存在有限数量的可能输入,因此它们都是可判定的.但是使用几乎与上述相同的结构很容易"证明"不能为所有值计算任何非平凡的`f`.例如,对于`fx = x> 0`,使用`impossibleValue = if(f impossibleValue)然后使用0 else 1 (3认同)
  • @CA McCann:这段代码并没有表明"没有'isInfinite`的可能实现可以在所有列表上工作",因为`impossibleList`不是一个列表(它是⊥).否则你可以说它也表明没有可能的'isNonEmpty`实现可以在所有列表上工作,正如我在之前的评论中所说的那样. (2认同)

scl*_*clv 12

isInfinite x = length x `seq` False
Run Code Online (Sandbox Code Playgroud)

  • 我会这样说......它从来没有给出错误的答案. (19认同)
  • @alexraasch:不,`seq`确保它计算长度 - 如果列表是无限的,那当然永远不会完成.因此,如果列表是有限的(这是正确的),它要么给出'False`,要么它永远不会完成,因此根本不回答.并且不存在的答案不能称为*不正确*,可以吗?:]是的,这是一个笑话,如果那不明显的话. (13认同)

小智 12

我们不需要解决停机问题来安全地调用'长度'.我们只需要保守; 接受所有具有有限性证据的东西,拒绝所有没有证据的东西(包括许多有限列表).这正是类型系统的用途,因此我们使用以下类型(t是我们忽略的元素类型):

terminatingLength :: (Finite a) => a t -> Int
terminatingLength = length . toList
Run Code Online (Sandbox Code Playgroud)

有限类只包含有限列表,因此类型检查器将确保我们有一个有限的参数.有限的成员资格将是我们有限的证明."toList"函数只是将有限值转换为常规Haskell列表:

class Finite a where
  toList :: a t -> [t]
Run Code Online (Sandbox Code Playgroud)

现在我们的实例是什么?我们知道空列表是有限的,所以我们创建一个数据类型来表示它们:

-- Type-level version of "[]"
data Nil a = Nil
instance Finite Nil where
  toList Nil = []
Run Code Online (Sandbox Code Playgroud)

如果我们'将'一个元素放在有限列表上,我们得到一个有限列表(例如,如果"xs"是有限的,则"x:xs"是有限的):

-- Type-level version of ":"
data Cons v a = Cons a (v a)

-- A finite tail implies a finite Cons
instance (Finite a) => Finite (Cons a) where
  toList (Cons h t) = h : toList t -- Simple tail recursion
Run Code Online (Sandbox Code Playgroud)

任何调用我们的terminatingLength函数的人现在必须证明他们的列表是有限的,否则他们的代码将无法编译.这并没有消除停机问题,但我们已将其转移到编译时而不是运行时.编译器可能在尝试确定有限元的成员资格时挂起,但这比生成程序在给出一些意外数据时挂起要好.

需要注意的是:Haskell的'ad-hoc'多态性允许在代码中的其他点声明几乎任意的有限实例,而终止长度将接受这些作为有限证明,即使它们不是.这不是太糟糕了; 如果有人试图绕过你的代码的安全机制,他们会得到他们应得的错误;)


Lar*_*ars 5

不 - 你最好估计一下.请参阅暂停问题.

  • @ShreevatsaR:采用任意的一般递归函数,将其重写为展开操作,生成每个递归步骤中的连续值列表和达到基本情况时的单例列表.如果原始函数终止,则列表将是有限的,但是任意的一般递归函数正是图灵机可计算的那些.因此,对任意列表起作用的`isFinite`函数必然是Halting oracle. (8认同)
  • 好吧,你可以创建一个列表,返回头部指向的图灵机中磁带上的当前元素.如果你可以说列表是否有限,你可以解决暂停问题. (7认同)
  • @Alexander Raasch:没有甲骨文.您在Haskell中构建了一个图灵机模拟器,它提供了一个显示头部移动方式的列表.列表结束:程序完成. (4认同)