如何在Haskell中实现类似JavaScript的取模(支持非整数与符号)?
实现与JavaScript一致的Haskell取模函数
JavaScript的取模行为
JavaScript中%运算符的结果符号与被除数一致,具体行为如下:
console.log(13 % 5); // Expected output: 3 console.log(-13 % 5); // Expected output: -3
我需要在Haskell中实现完全相同的逻辑,目前编写了以下代码:
:{ modulo :: Double -> Int -> Double modulo a p = let p' = fromIntegral p in if a > 0 then a - fromIntegral(p * floor(a/p')) else a - fromIntegral(p * ceiling(a/p')) :}
这段代码能得到和JavaScript一致的结果,但我想知道有没有更简洁高效的实现方式。需要注意的是,该函数必须支持Double类型的被除数a,和Haskell内置的rem不同,比如:
ghci> modulo (-13.5) 5 -3.5
更简洁的实现方式
JavaScript的%本质是余数运算,结果符号与被除数一致,等价于公式:a - b * Math.trunc(a / b)。对应到Haskell中,Math.trunc可以用truncate函数实现,因此可以简化代码为:
modulo :: Double -> Int -> Double modulo a p = let p' = fromIntegral p in a - p' * fromIntegral (truncate (a / p'))
测试验证
- 正数场景:
modulo 13 5→13 - 5 * truncate(2.6)→13-10=3,符合预期。 - 负数被除数场景:
modulo (-13) 5→-13 -5 * truncate(-2.6)→-13 +10=-3,符合预期。 - 小数被除数场景:
modulo (-13.5)5→-13.5 -5 * truncate(-2.7)→-13.5+10=-3.5,符合预期。
如果需要支持更多数值类型(比如Float作为被除数,Integer作为除数),可以泛化类型签名:
modulo :: (RealFrac a, Integral b) => a -> b -> a modulo a p = let p' = fromIntegral p in a - p' * fromIntegral (truncate (a / p'))
内容的提问来源于stack exchange,提问作者Stéphane Laurent
相关产品推荐
相关产品推荐

