为什么乘法只在一边短路
我fix
,搞乱之后我碰到了一些奇怪的行为,即0 * undefined
是*** Exception: Prelude.undefined
和undefined * 0
是0
。 这也意味着fix (0 *)
是*** Exception: <<loop>>
和fix (* 0)
是0
。
玩过之后,似乎是因为在两个方向上进行短路并不是微不足道的,因为这没有什么意义,没有某种奇怪的平行计算,从第一个非平行计算开始,底部返回。
这种东西是否在其他地方出现(reflectionfunction不是底层价值的reflection),是我可以安全依靠的东西吗? 也有一个实际的方法,使(0 *)
和(* 0)
评估为零,无论传入的值。
你的推理是正确的。 有一个unamb
包提供了你所指的那种并行计算的工具。 实际上,它提供了Data.Unamb.pmult
,它并行地尝试检查每个操作数是1还是0,如果是的话立即产生一个结果。 对于简单的算术,这种并行方法在大多数情况下可能要慢得多!
(*)
的短路仅在GHC版本7.10中出现。 这是由于GHC版本中Integer
types的实现发生了变化的结果。 这个额外的懒惰一般被看作是一个性能缺陷 (因为它会干扰严格性分析,甚至会导致理论上的空间泄露),所以在GHC 8.0中将会被删除。
以下面的例子
(if expensiveTest1 then 0 else 2) * (if expensiveTest2 then 0 else 2)
你必须select一个方面进行评估。 如果expensiveTest2
是一个无限循环,你将永远无法辨别右侧是否为0
,所以你不能分辨是否将右侧短路,所以你永远不会看左侧。 你不能一次检查双方是否为0
。
至于是否可以依靠短路作用,请记住,只要不使用IO, undefined
和error
就像无限循环一样。 因此,您可以使用undefined
和error
来testing短路和懒惰。 一般来说,短路的行为因function而异。 (也有不同程度的懒惰, undefined
, Just undefined
可能会给出不同的结果。)
看到这个更多的细节。
其实,看起来fix (* 0) == 0
只适用于Integer
,如果你运行fix (* 0) :: Double
或者fix (* 0) :: Int
,你仍然会得到***Exception <<loop>>
这是因为在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 yx -- no S# as first arg from here on timesInteger (Jp# x) (Jp# y) = Jp# (timesBigNat xy) timesInteger (Jp# x) (Jn# y) = Jn# (timesBigNat xy) 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 xy) timesInteger (Jn# x) (Jp# y) = Jn# (timesBigNat xy) timesInteger (Jn# x) (S# y#) | isTrue# (y# >=# 0#) = Jn# (timesBigNatWord x (int2Word# y#)) | True = Jp# (timesBigNatWord x (int2Word# (negateInt# y#)))
看看上面的代码,如果你运行(* 0) x
,那么timesInteger _ (S# 0#)
会匹配,所以x
不会被计算,而如果你运行(0 *) x
,那么当检查是否timesInteger _ (S# 0#)
匹配,x将被评估并导致无限循环
我们可以使用下面的代码来testing它:
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
GHCI有一些更有趣的事情:
*Test> let x = fix (* 0) *Test> x 0 *Test> x :: Double *** Exception: <<loop>> *Test>