正数按位列表减法借位问题:现有Haskell实现返回错误值如何修复
问题根因
你当前代码的核心问题是sub_Carry函数完全没有用到传入的借位参数c,也没有处理单位相减为负后的借位逻辑,直接返回x-y的原始结果,才会出现负数位。另外负数场景你直接用小值减大值的逻辑也存在问题,应该先反向计算大值减小值再补负号标识。
修复方案
调整逻辑如下:
- 单位计算前先扣除上一级传递来的借位
c - 若当前位相减结果小于0,给结果加10,同时设置下一级的借位为1,否则下一级借位为0
- 列表遍历完的边界分支也要处理剩余未抵消的借位
- 负数场景用大值减小值后再补前导
-1标识
修改后完整代码:
sub_Carry :: Integer -> [Integer] -> [Integer] -> [Integer] -- 减数遍历完,处理剩余被减数和借位 sub_Carry c x [] | c == 0 = x | null x = [1] | otherwise = let (h:t) = x diff = h - c in if diff < 0 then (diff + 10) : sub_Carry 1 t [] else diff : sub_Carry 0 t [] -- 被减数遍历完,处理剩余减数和借位 sub_Carry c [] x | c == 0 = x | null x = [1] | otherwise = let (h:t) = x diff = h - c in if diff < 0 then (diff + 10) : sub_Carry 1 t [] else diff : sub_Carry 0 t [] -- 双列表都有值的核心计算逻辑 sub_Carry c (x : xs) (y : ys) = let curr = x - c diff = curr - y in if diff < 0 then (diff + 10) : sub_Carry 1 xs ys else diff : sub_Carry 0 xs ys sub :: [Integer] -> [Integer] -> [Integer] sub op1 op2 | to_Integer(op1) == to_Integer(op2) = [0] | to_Integer(op1) > to_Integer(op2) = drop_Zeros (reverse (sub_Carry 0 (reverse op1) (reverse op2))) | otherwise = [-1] ++ drop_Zeros (reverse (sub_Carry 0 (reverse op2) (reverse op1)))
效果验证
拿你举的例子[1,2,0] - [1,0,1]验证:
- 先判断
120 > 101,走正数分支 - reverse两个参数得到
[0,2,1]和[1,0,1]传入sub_Carry 0 - 第一位计算:
0 - 0 -1 = -1,加10得9,下一级借位1,返回第一位9 - 第二位计算:
2 - 1 - 0 =1,无借位,返回第二位1 - 第三位计算:
1 -0 -1=0,无借位,返回第三位0 sub_Carry返回结果[9,1,0],reverse后得到[0,1,9]drop_Zeros去掉前导零得到[1,9],完全符合预期。
内容的提问来源于stack exchange,提问作者Sophia S.
相关产品推荐
相关产品推荐

