底部本质上是一种高级的代数方式来表示undefined
。
如果你尝试这样做,你会发现为什么zip
对于它的右侧参数是惰性的:
λ> zip [] undefined
[]
λ> zip undefined []
*** Exception: Prelude.undefined
这是因为undefined
只有在试图求值时才会失败。
你可能会因为它的呈现方式而将_|_
与_
混淆。我会让它变得更清晰:行zip [] _|_ = []
不是一个模式匹配,而是一个等式,说明了 zip [] _|_
和[]
的相等性。也就是说,这不是有效的 Haskell 代码,而是一种符号、抽象代数方式来表示“我不关心第二个参数”。
在zip
的定义中,你当然可以使用_
,但这并不重要。你可以使用任何名称,只要它不是构造函数匹配模式,例如(Just x)
或(a,b)
。在纯代码中,值将保持为未求值状态,直到必须进行模式匹配。
你可以在这里了解更多关于惰性求值的信息。
zip
定义中的某个地方,由于zip
代码的编写方式,对于左参数与[]
的匹配会以一个[]
结果结束(在右参数被计算之前)。 对吗?(而文档只是描述了这种后果。) - oromezip [] _ = []
(在任何其他 zip [] y
匹配之前,这将触发对 y
的评估)。对吧? - orome_|_
只是⊥
的ASCII等效符号。这是一个数学符号。对于Haskell值,底部就像无限计算或异常(基本上以相同的方式处理)。它是类型为a
的值(即任何类型)。这与具体类型的值形成对比。 - Bakuriuzip [] _|_ = []
不是实际的代码!符号|_|
(这只是数学符号⊥
的ASCII图形呈现)表示bottom1,但仅当我们讨论Haskell时才具有此含义2。在Haskell代码中,它没有这个意思。 zip [] _|_ = []
是实际的zip
代码的属性描述;如果您使用第一个参数[]
调用它,并将任何bottom值作为第二个参数传递,则结果等于[]
。他们想要明确说明这一点的原因是因为函数f
被非严格定义的技术定义是当f ⊥
不是⊥
时。但是,在定义Haskell函数(在代码中)时,没有|_|
(或⊥
或未定义
或bottom概念)的作用。出于许多原因,必须不可能对参数进行模式匹配以查看它是否为⊥
,因此在Haskell代码中不存在实际的⊥
符号3。zip [] _|_ = []
是一个属性的文档,该属性是zip
定义的结果,而不是它的一部分。作为对这个属性的描述,zip [] _ = []
是一个更不具体的声明;它将表明您调用zip []
的任何内容,它都返回[]
。它等同于完全相同的事情,因为只有当它根本不检查第二个参数时,zip [] ⊥
才能返回非bottom的东西。但是,它对非严格性的定义说话的效果较弱。作为函数zip [] _ = []
定义的一部分的代码不能与zip [] _|_ = []
进行比较和对比。 它们不是替代方案,第一个是有效的代码,第二个则无效。_
)和“运算符”字符(|
)。因此,在Haskell代码中,它实际上不能成为表示任何东西的符号!
3 undefined
经常用于表示 ⊥
,但它更像是指向一个 ⊥
值的变量,而不是实际的值本身。就像如果你有 let xs = [1, 2, 3]
,你可以使用 xs
来引用列表 [1, 2, 3]
,但你不能将其用作与其他列表匹配的模式; 尝试的模式匹配将被视为引入一个名为 undefined
或 xs
的新变量,覆盖旧变量。
参考AJFarmar的答案,我认为这一关键点并没有被明确说明:
_|_
不是Haskell代码中有效的文字或标识符!zip [] _|_ = []
也不是有效的代码!这就是AJFarmar引用中隐含的意思:
[T]he line
zip [] _|_ = []
does not act as a pattern match but an equation, stating the equality ofzip [] _|_
and[]
.
为了让它非常清楚明白,zip [] _|_ = []
出现在zip
的文档注释中。它不是Haskell代码 - 它是用类似Haskell代码的非正式技术符号编写的英语注释。换句话说,它是伪代码。
_bs
是什么意思?它和 _
一样吗?而且,模式 zip [] _bs = ...
是否确保如果第一个参数是[]
(且右侧没有提到第二个参数),则第二个参数不会被评估? - oromex
这样的模式和特殊模式 _
之间唯一的区别是 _
永远不会出现在右侧。你可以写 f x = x
但不能写 f _ = _
。在 GHC 中,有一个警告可用,通过 -Wall
等启用,它会警告未使用的绑定。在 GHC 中,以下划线开头的名称绑定会抑制该警告。因此,您可以编写 f _x = 3
来建议传入的是 x
,但告诉编译器和人类读者您确实不打算使用它。 - dfeuerseq
和evaluate
函数,以及调用这些函数的函数,if-then-else
(强制条件)和各种数字操作。 (还有一个GHC扩展名为bang patterns) - dfeuer⊥
,当zip [] b
对于任何 b
都将是[]
并且会使b
未评估。⊥
的情况是否满足某些重要的数学属性(另一个被忽略/未评估的值不会)? - oromef x = let z = x + 3 in 7
不会评估 x
,因为 x
只用于定义 z
,而且没有人会查看 z
。将其更改为 f x = let z = x + 3 in if z > 4 then 12 else 13
,突然间 f
对 x
是严格的。 - dfeuer_bs
只是一个变量,但它有一个小技巧:GHC的warn-unused-binds
功能会警告未使用的变量,但跳过以下划线开头的变量。所以问题中发生的情况是,编译该选项的代码,并且使用该名称关闭了警告。为什么不直接使用_
呢?我不知道作者的动机,但几乎肯定是出于风格考虑。 - Luis Casillas⊥源自数学序理论。如果一个偏序集合有一个底部元素,表示为⊥,则该元素在其他所有元素之前。这如何出现在Haskell文档中?在某个时刻,计算机科学家意识到思考计算机程序(无论使用哪种语言)的“含义”将非常有用。其中一种方法称为指示语义。在指示语义中,编程语言中的每个术语都被分配了一个“指示”,或者说是某个数学含义的意思。例如,能够说出以下内容将是很棒的:
meaningInteger :: Integer -> 数学整数
meaningList :: [a] -> 可能是无限类型为a的元素序列
不幸的是,在Haskell中这并不完全适用,因为我可以这样写:
oops :: Integer
oops = oops
这给了我一个类型为整数(Integer)
的术语,但是没有明显的方法将其赋予数学整数的含义。更有趣的是,我可以这样写:
undefined
undefined : undefined
3 : undefined
[undefined]
let foo = undefined : 3 : undefined : foo
这些都可以拥有相同的类型,但是存在各种不同程度的未定义性。因此,我们需要添加各种未定义事物的含义到我们的集合中。然而,我们可以基于它们的定义程度对它们强制实施“偏序”。例如,3:4:[]
比3:4:undefined
更加定义,并且比3:undefined:4
更加定义,但是后面两个无法比较。每种类型的底部元素,即其最少定义的元素,称为⊥。
3:4: []
比3:4:undefined
更明确定义,并且也比3:undefined:4
更明确定义,但后两者不可比较。” 我理解为3:4:undefined
和3:undefined:4
都是未定义的,两者都没有比另一个更未定义,因为“未定义”中没有区别,这显然与我对先前示例的理解相矛盾。 - Praxeolitic3 : undefined : 4
不是良好类型化的。为了保存您的示例,您可以将其替换为 3 : undefined : []
。(3 : undefined : 4 : []
与 3 : 4 : []
不可比较,而 3 : undefined
没有 3 : 4 : undefined
定义得清楚。) - peter punx
为_|_
或原子且等于y
,或者可以与y
对齐并且其所有组件都比y
中对应的组件不完全定义时,则x
比y
不完全定义。当然,“原子”,“组件”和“对齐”之类的东西需要定义(这似乎不太难制定)。 - peter pun