例如,我有一个操作fnB :: a -> Bool
,在fnA :: Bool
返回False
之前没有任何意义。在C中,我可以在一个if
块中组合这两个操作:
if( fnA && fnB(a) ){ doSomething; }
和C将保证fnB
在fnA
返回false之前不会执行。
但是Haskell是懒惰的,并且通常不能保证首先执行什么操作,直到我们不使用seq
,$!
或其他东西来使我们的代码严格。一般来说,这就是我们需要快乐的事情。但是使用&&
运算符,我希望在fnB
返回结果之前不会评估fnA
。 Haskell是否为&&
提供了这样的保证?即使fnB
返回False,Haskell也会评估fnA
吗?
答案 0 :(得分:32)
只有当第一个参数为(&&)
时,函数True
的第二个参数才是严格的。它的第一个论点总是严格的。这种严格/懒惰是保证评估顺序的原因。
因此它的行为与C完全相同。不同之处在于,在Haskell中,(&&)
是一个普通的函数。在C中,这是不可能的。
但是Haskell是懒惰的,并且通常不能保证首先执行什么操作,直到我们不使用seq,$!或其他东西来严格执行我们的代码。
这不正确。事实更深刻。
严格的崩溃课程
我们知道(&&)
的第一个参数是严格的,因为:
⊥ && x = ⊥
这里,⊥类似undefined
或无限循环(⊥发音为“bottom”)。我们也知道(False &&)
在第二个参数中是非严格的:
False && ⊥ = False
它不可能评估它的第二个参数,因为它的第二个参数是⊥,它不能被评估。但是,函数(True &&)
在其第二个参数中是严格的,因为:
True && ⊥ = ⊥
因此,我们说(&&)
在第一个参数中始终是严格的,而在第二个参数中仅在第一个参数为True
时才严格。
评估顺序:
对于(&&)
,其严格性属性足以保证执行顺序。情况并非总是如此。例如,(+) :: Int -> Int -> Int
在两个参数中始终是严格的,因此可以首先计算任一参数。但是,您只能通过在IO
monad中捕获异常,或者使用unsafe
函数来区分异常。
答案 1 :(得分:5)
正如其他人所指出的,自然(&&)
在其中一个论点中是严格的。按照标准定义,它的第一个论点是严格的。您可以使用flip
来翻转语义。
作为补充说明:请注意(&&)
的参数不会产生副作用,因此只有两个原因可以解释x && y
y
中y
是否严格的原因:
y
需要很长时间才能计算。答案 2 :(得分:1)
Haskell很懒,而且一般来说,无法保证首先执行什么操作
不完全。 Haskell是纯(除了unsafePerformIO
和IO
的实现),并且没有办法观察哪个操作将首先执行(除了unsafePerformIO
和IO
的实施。 的执行顺序与结果无关。
&&
有一个9值的真值表,包括一个或两个参数都是undefined
的情况,它确切地定义了操作:
a b a && b
True True True
True False False
True undefined undefined
False True False
False False False
False undefined False
undefined True undefined
undefined False undefined
undefined undefined undefined
只要实现遵循该表,就可以按任何顺序执行操作。
(如果您研究该表,您会注意到顺序实现无法遵循它,除非它首先执行a
,然后b
iff { {1}}是真的。但是Haskell实现不需要是顺序的!一个实现总是允许随时执行a
;你唯一的保证是,根据在表中,执行b
的结果只会在b
为True时影响您的程序。)
(注意'懒惰'是使用如上所述的真值表编写函数的唯一方法;在像C或ML这样的语言中,所有五行都带有{{任何一个参数中的1}}都会强制a
作为结果,在Haskell中(在C中,因为undefined
内置于C语言中)其中一行可以undefined
结果是1}}。)
答案 3 :(得分:-1)
我相信它的工作方式与你期望的一样;如果LHS评估为True,则评估RHS。但是,假设RHS没有副作用,你怎么知道(或关心)?
编辑:我猜RHS可能是undefined
,然后你会关心......