簡體   English   中英

如何對兩個列表的元素求和。 Haskell

[英]How to sum elements of two lists. Haskell

我才剛剛開始學習Haskell,目前正在探索列表的可能性。 我想總結兩個列表,但不知何故出錯了。

所以:

輸入:sumTwoLists [2,5,7,7,9] [1,2,2](基本上是 25779 + 122)

Output:[2,5,9,0,1]

首先,我將整個列表顛倒過來,因為多位數字的添加必須從末尾開始:

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

有用。 然后我實現了一個add function:

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

但是當一個列表比另一個列表短時,它就會出錯。 (add [2,5,7,7,9] [1,2,2] = [3,7,9]) 所以finction也必須在小數后面加0 ([1,2,2] = [1,2,2,0,0]。)

之后,我嘗試像這樣實現 sumTwoLists function:

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. 考慮到將兩位數相加可能會導致值大於9並因此引入進位

add function 無法正常工作,因為它從列表之一用盡時停止,此外它沒有考慮到數字可能“溢出”。 因此,我們應該構造一個 function 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] 您實現的reverseList function 在O(n 2 )中運行,效率不高,您可以使用累加器來獲得線性時間。

這是以 10 為底的進數和。這是一個實現。

-- 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應用於反轉列表並反轉 output:

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

這個答案將使用 Haskell 的內置reverse而不是您的reverseList ,盡管它們是可以互換的。

讓我們先看看您添加的 function:

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。 Remember that mod is the remainder of division, so 12 `mod` 10 ( backticks in Haskell makes a function infix ) is 2 , and 12 `div` 10 , due to the nature of integer division rounding down, will give you every digit after第一個數字,即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)

現在,您的助手 function 可以使用 0 作為初始進位調用它:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM