如何对两个列表的元素求和.哈斯克尔



我才刚刚开始学习Haskell,目前我正在探索列表的可能性。我想总结两个清单,但不知怎么出了问题。

因此:

输入:sumTwoLists[2,5,7,7,9][1,2,2](基本为25779+122(

输出:[2,5,9,0,1]

首先,我颠倒了整个列表,因为多位数的添加必须从末尾开始:

reverseList :: [Int] -> [Int]
reverseList [] = []
reverseList (x:xs) = reverseList xs ++ [x]

它有效。然后我实现了一个添加功能:

add :: (Num a) => [a] -> [a] -> [a]
add _ [] = []
add [] _ = []
add (x:xs) (y:ys) = (x + y) : add xs ys

但是,当一个列表比另一个列表短时,就会出错。(加[2,5,7,7,9][1,2,2]=[3,7,9](因此fintion还必须在较低数字的末尾加0([1,2,2]=[1,2,2,0](

在那之后,我尝试实现sumTwoLists函数,如下所示:

sumTwoLists :: [Int] -> [Int] -> [Int]
sumTwoLists (x:xs) (y:ys) = reverseList ((reverseList (x:xs)) add (reverseList (y:ys)))

但此代码没有考虑元素不能大于9这一事实。我不想将元素转换为Int或Integer,这就是为什么我不使用它们中的任何一个

我基本上只想颠倒列表,然后在最短的列表中添加0,然后将每个元素与另一个列表中的一个元素相加,如果结果是>9,然后将结果除以10(mod?(,并增加相邻编号的

如果有任何帮助,我将不胜感激!

对列表这样做没有多大意义,因为它会引入很多额外的问题:

  1. 如果两个列表的长度不相同,或者总和需要额外的元素,则用零填充;以及
  2. 考虑到将两个数字相加可能导致大于CCD_ 1的值,并因此引入进位

add函数工作不正常,因为它从其中一个列表用完的那一刻起就停止了,而且它没有考虑到数字可以";溢出";。因此,我们应该构造一个函数add,它有一个额外的参数:进位:

add' :: Int -> [Int] -> [Int] -> [Int]
add' 0 [] [] = []
add' n [] [] = [n]
add' n xs [] = add' n xs [0]
add' n [] xs = add' n [0] xs
add' n (x:xs) (y:ys) = r : add' q xs ys
where (q,r) = quotRem (x+y+n) 10

add因此以零作为进位开始:

add :: [Int] -> [Int] -> [Int]
add = add' 0

如果我们这样计算反向列表的总和,我们得到:

Prelude> add [9,7,7,5,2] [2,2,1]
[1,0,9,5,2]

为了实现这一点,您需要使用add作为中缀运算符,并且两个操作数可以是空列表或非空列表:

sumTwoLists :: [Int] -> [Int] -> [Int]
sumTwoLists xs ys = reverseList ((reverseList xs)`add`(reverseList ys))

或使用on :: (b -> b -> c) -> (a -> b) -> a -> a -> c

import Data.Function(on)
sumTwoLists :: [Int] -> [Int] -> [Int]
sumTwoLists = add`on`reverse

对于给定的样本输入,我们现在得到:

Prelude> sumTwoLists [2,5,7,7,9] [1,2,2]
[2,5,9,0,1]

最后,reverseList已经存在于Prelude:reverse :: [a] -> [a]中。您实现的90函数在O(n2(中运行,这不是很有效,您可以使用累加器来获得线性时间。

这是基数为10的adic和。下面是一个实现。

-- odometer (add one in base b)
odom :: Int -> [Int] -> [Int]
odom b (x : xs) | x<(b-1) = (x+1) : xs
| xs==[] = [0,1]
| otherwise = 0 : odom b xs
-- iterated odometer
odom_iter :: Int -> [Int] -> Int -> [Int]
odom_iter b t n | n == 0 = t
| otherwise = odom b (odom_iter b t (n-1))
-- adic addition
sumadic :: Int -> [Int] -> [Int] -> [Int]
sumadic b (x:xs) (y:ys) | xs == [] = odom_iter b (y:ys) x
| ys == [] = odom_iter b (x:xs) y
| sumxy < b = (sumxy : (sumadic b xs ys))
| sumxy == b = (0 : (odom b (sumadic b xs ys)))
| otherwise = (1 : (odom b (sumadic b xs ys)))
where sumxy = x+y

这是通过颠倒列表来实现的:

> sumadic 10 [9, 7, 7, 5, 2] [2, 2, 1]
[1,0,9,5,2]

因此,您只需要将sumadic应用于反向列表并反向输出:

sumTwoLists x y = reverse $ sumadic 10 (reverse x) (reverse y)

这个答案将使用Haskell内置的reverse,而不是您的reverseList,尽管它们是可互换的。

让我们从您的添加功能开始:

add :: (Num a) => [a] -> [a] -> [a]
-- look at these next 2 lines specfically
add _ [] = []
add [] _ = []
add (x:xs) (y:ys) = (x + y) : add xs ys

如果您将一个非空列表添加到一个空列表中,那么您当前的代码会说它是空列表,这显然不是真的(add [1, 2, 3] []应该是[1, 2, 3](。因此,您可以首先通过返回另一个列表来修复您的基本情况:

add :: (Num a) => [a] -> [a] -> [a]
-- return the other list
add [] x = x
add x [] = x
add (x:xs) (y:ys) = (x + y) : add xs ys

如果添加了两个空列表,那么另一个列表就是空列表,所以您仍然正确地返回了空列表。现在,我们可以解决";该代码没有考虑元素不能大于9〃的事实;部分既然你的加法方法是模拟你如何在纸和笔上进行加法,那就继续这样做吧。例如,如果结果是12,那么数字是2,并且1被进位。请记住,mod是除法的余数,因此12 `mod` 10(Haskell中的backticks使函数中缀(是2,而12 `div` 10,由于整数除法向下取整的性质,将给出第一个数字之后的每个数字,即1

说够了,让我们写一些代码:

-- change Num to Integral because we need to work with integers
add :: (Integral a) => [a] -> [a] -> a -> [a]
--        we need to add a carry now ^
-- these base cases break down if carry is non-zero
add [] x c
-- if carry is zero we're fine
| c == 0    = x
-- just add the carry in as a digit
| otherwise = add [c] x 0
-- same applies here
add x [] c
| c == 0    = x
| otherwise = add x [c] 0
add (x:xs) (y:ys) c = dig : add xs ys rst
where sum = x + y + c    -- find the sum of the digits plus the carry
-- these two lines can also be written as (rst, dig) = sum `divMod` 10
dig = sum `mod` 10 -- get the last digit
rst = sum `div` 10 -- get the rest of the digits (the new carry)

现在,你的助手函数可以用0作为初始进位来调用它:

addTwoLists :: (Num a) => [a] -> [a] -> [a]
addTwoLists x y = reverse $ add (reverse x) (reverse y) 0

最新更新