使用以下程序,
f 0 0 0 1 = 0
f 0 0 1 0 = f 0 0 0 1 + 1
f 0 1 0 0 = f 0 0 1 1 + 1
f 1 0 0 0 = f 0 1 1 1 + 1
f a b c d = (p + q + r + s) / (a + b + c + d)
where
p
| a > 0 = a * f (a - 1) (b + 1) (c + 1) (d + 1)
| otherwise = 0
q
| b > 0 = b * f a (b - 1) (c + 1) (d + 1)
| otherwise = 0
r
| c > 0 = c * f a b (c - 1) (d + 1)
| otherwise = 0
s
| d > 0 = d * f a b c (d - 1)
| otherwise = 0
main = print (f 1 1 1 1)
我认为它可以简化为,
f 0 0 0 1 = 0
f 0 0 1 0 = f 0 0 0 1 + 1
f 0 1 0 0 = f 0 0 1 1 + 1
f 1 0 0 0 = f 0 1 1 1 + 1
f a b c d = (p + q + r + s) / (a + b + c + d)
where
p = a * f (a - 1) (b + 1) (c + 1) (d + 1)
q = b * f a (b - 1) (c + 1) (d + 1)
r = c * f a b (c - 1) (d + 1)
s = d * f a b c (d - 1)
main = print (f 1 1 1 1)
因为除了两者在数学上都是合理的声音之外,我认为通过懒惰的评估,编译器或解释器应该能够决定将任何东西乘以0是不必要的。但是,该程序确实进入了无限循环。为什么这样?
答案 0 :(得分:5)
内置乘法在两个参数中都是严格的 - 也就是说,它会计算两个参数 - 无论其中一个是否为零,这都是导致程序循环的原因。你可以定义自己的乘法运算符,它懒惰地消除了一个或另一个参数:
0 .* y = 0
x .* y = x * y
或者相反。定义一个在两侧消除零的运算符需要更多一点,但可以使用unamb
包来完成:
x .* y = unambs [ assuming (x == 0) 0
, assuming (y == 0) 0
, x * y
]
虽然据我所知,这还没有足够可靠的实施: - /。
答案 1 :(得分:5)
除了定义你自己的乘法运算符@luqui建议,你可以定义你自己的内置乘法短路类型:
newtype SCZero a = SCZero a
deriving Eq
instance Show a => Show (SCZero a) where
show (SCZero x) = show x
instance (Eq a, Num a) => Num (SCZero a) where
SCZero x + SCZero y = SCZero (x + y)
SCZero 0 * SCZero y = SCZero 0
SCZero x * SCZero y = SCZero (x * y)
abs (SCZero x) = SCZero (abs x)
signum (SCZero x) = SCZero (signum x)
fromInteger x = SCZero (fromInteger x)
negate (SCZero x) = SCZero (negate x)
instance (Eq a, Fractional a) => Fractional (SCZero a) where
fromRational x = SCZero (fromRational x)
SCZero 0 / SCZero y = SCZero 0
SCZero x / SCZero y = SCZero (x / y)
然后,您可以直接使用现有代码,只需将结果类型指定为SCZero
:
*Main> print (f 1 1 1 1 :: SCZero Double)
0.464398781601087