sem*_*lon 47 haskell short-circuiting
我是用插科打诨fix
和乱搞它后,我遇到了一些怪异的行为来,即0 * undefined
是*** Exception: Prelude.undefined
和undefined * 0
是0
.这也意味着,fix (0 *)
是*** Exception: <<loop>>
和fix (* 0)
是0
.
在玩它之后,似乎原因是因为在两个方向上使它短路是非常重要的,因为这没有多大意义,没有某种奇怪的并行计算并从第一个非开始底部回来了.
这种事情在其他地方是否可见(反身函数对底值不反身),是否可以安全依赖?也有做出既切实可行的办法(0 *)
和(* 0)
评估,无论传入的值为零.
dfe*_*uer 41
你的推理是正确的.有一个unamb
包提供了您所参考的并行计算的工具.事实上,它提供Data.Unamb.pmult
并行尝试检查每个操作数是1还是0,如果是,立即产生结果.对于简单算术,这种并行方法在大多数情况下可能要慢得多!
短路仅(*)
发生在GHC版本7.10中.它是由于GHC版本中类型实现的变化而产生的.这种额外的懒惰通常被视为性能缺陷(因为它会干扰严格性分析,甚至可能导致理论上的空间泄漏),因此它将在GHC 8.0中删除.Integer
以下面的例子为例
(if expensiveTest1 then 0 else 2) * (if expensiveTest2 then 0 else 2)
Run Code Online (Sandbox Code Playgroud)
您必须选择要评估的一方.如果expensiveTest2
是一个无限循环,你永远无法分辨右边是否是正确的0
,所以你无法判断右边是否短路,所以你永远不会看到左侧.您无法检查双方是否同时进行0
.
至于是否可以依靠短路以某种方式行事,只记住undefined
和error
行为完全相同,只要喜欢一个无限循环,你不使用IO.因此,您可以使用undefined
和测试短路和懒惰error
.通常,短路行为因功能而异.(也有不同程度的懒惰.undefined
并且Just undefined
可以给出不同的结果.)
有关详细信息,请参阅此
实际上,它似乎fix (* 0) == 0
只适用于Integer
,如果你跑,fix (* 0) :: Double
或者fix (* 0) :: Int
你仍然得到***Exception <<loop>>
那是因为in instance Num Integer
,(*)
被定义为(*) = timesInteger
timesInteger
定义于 Data.Integer
-- | Multiply two 'Integer's
timesInteger :: Integer -> Integer -> Integer
timesInteger _ (S# 0#) = S# 0#
timesInteger (S# 0#) _ = S# 0#
timesInteger x (S# 1#) = x
timesInteger (S# 1#) y = y
timesInteger x (S# -1#) = negateInteger x
timesInteger (S# -1#) y = negateInteger y
timesInteger (S# x#) (S# y#)
= case mulIntMayOflo# x# y# of
0# -> S# (x# *# y#)
_ -> timesInt2Integer x# y#
timesInteger x@(S# _) y = timesInteger y x
-- no S# as first arg from here on
timesInteger (Jp# x) (Jp# y) = Jp# (timesBigNat x y)
timesInteger (Jp# x) (Jn# y) = Jn# (timesBigNat x y)
timesInteger (Jp# x) (S# y#)
| isTrue# (y# >=# 0#) = Jp# (timesBigNatWord x (int2Word# y#))
| True = Jn# (timesBigNatWord x (int2Word# (negateInt# y#)))
timesInteger (Jn# x) (Jn# y) = Jp# (timesBigNat x y)
timesInteger (Jn# x) (Jp# y) = Jn# (timesBigNat x y)
timesInteger (Jn# x) (S# y#)
| isTrue# (y# >=# 0#) = Jn# (timesBigNatWord x (int2Word# y#))
| True = Jp# (timesBigNatWord x (int2Word# (negateInt# y#)))
Run Code Online (Sandbox Code Playgroud)
看看上面的代码,如果你运行(* 0) x
,那么timesInteger _ (S# 0#)
会匹配以便x
不会被评估,而如果你运行(0 *) x
,那么当检查是否timesInteger _ (S# 0#)
匹配时,x将被评估并导致无限循环
我们可以使用下面的代码来测试它:
module Test where
import Data.Function(fix)
-- fix (0 ~*) == 0
-- fix (~* 0) == ***Exception<<loop>>
(~*) :: (Num a, Eq a) => a -> a -> a
0 ~* _ = 0
_ ~* 0 = 0
x ~* y = x ~* y
-- fix (0 *~) == ***Exception<<loop>>
-- fix (*~ 0) == 0
(*~) :: (Num a, Eq a) => a -> a -> a
_ *~ 0 = 0
0 *~ _ = 0
x *~ y = x *~ y
Run Code Online (Sandbox Code Playgroud)
在GHCI中有一些更有趣的东西:
*Test> let x = fix (* 0)
*Test> x
0
*Test> x :: Double
*** Exception: <<loop>>
*Test>
Run Code Online (Sandbox Code Playgroud)