簡體   English   中英

Haskell - 總結列表的前 n 個元素

[英]Haskell - Sum up the first n elements of a list

我是 Haskell 的新手。

假設我想用自己生成的函數總結列表的前n 個元素。 我不知道如何用 Haskell 做到這一點。 我只知道如何總結整個給定的列表,例如

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

為了總結一個列表的前n 個元素,例如

[1..10]取出前5 個數字,即1+2+3+4+5 = 15

我以為我可以做這樣的事情:

sumList :: Int -> [Int] -> Int
sumList take [] = 0
sumList take (x:xs) = x + take $ sumList xs

但它不起作用......怎么了?

所以你知道如何總結列表中的數字,

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

如果該列表中的元素不超過 5 個,並且您確實打算對參數列表中的元素求和不超過 5 個,則該函數甚至會返回正確的結果。 讓我們通過重命名這個函數來明確我們的期望,

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

對於長度超過五個的列表,它不會返回正確的結果,但至少名稱是正確的。

我們能解決這個問題嗎? 我們可以數到 5 嗎? 我們可以在沿着輸入列表前進的同時數到 5 嗎?

sumUpToFiveElements :: Int -> [Int] -> Int
sumUpToFiveElements counter [] = 0
sumUpToFiveElements counter (x:xs) = x + sumUpToFiveElements (counter + 1) xs

這當然還是不對的。 我們現在計數,但出於某種原因我們忽略了計數器。 如果我們想要不超過 5 個元素,什么時候對計數器做出反應? 讓我們試試counter == 5

sumUpToFiveElements :: Int -> [Int] -> Int
sumUpToFiveElements 5       [] = 0
sumUpToFiveElements counter [] = 0
sumUpToFiveElements counter (x:xs) = x + sumUpToFiveElements (counter + 1) xs

但是為什么我們要求列表在達到 5 時也為空? 我們不要那樣做:

sumUpToFiveElements :: Int -> [Int] -> Int
sumUpToFiveElements 5       _  = 0        -- the wildcard `_` matches *anything*
sumUpToFiveElements counter [] = 0
sumUpToFiveElements counter (x:xs) = x + sumUpToFiveElements (counter + 1) xs

成功! 我們現在在達到 5 時停止計數! 更多,我們也停止求和!!

等等,但是counter的初始值是多少? 我們沒有指定它,所以我們函數的用戶(也就是我們自己)很容易出錯並使用不正確的初始值。 順便說一句,什么正確的初始值?

好的,讓我們這樣做:

sumUpToFiveElements :: [Int] -> Int
sumUpToFiveElements xs = go 1 xs     -- is 1 the correct value here?
  where
    go counter  _ | counter == 5 = 0  
    go counter [] = 0
    go counter (x:xs) = x + go (counter + 1) xs

現在我們沒有那個使我們的定義變得如此脆弱、如此容易出現用戶錯誤的無關論點。

現在是重點:

概括! (通過用符號值替換示例值;將5更改為n )。

sumUpToNElements :: Int -> [Int] -> Int
sumUpToNElements n xs = .......
   ........

完畢。


還有一句忠告:不要在剛開始學習 Haskell 時使用$ 使用顯式括號。

sumList take (x:xs) = x + take $ sumList xs 

被解析為

sumList take (x:xs) = (x + take) (sumList xs)

這將兩個不相關的數字相加,然后將結果用作要調用的函數,以(sumList xs)作為參數(換句話說,這是一個錯誤)。

如果您使用顯式括號,您可能不會那樣寫。

好吧,您應該使用參數限制值的數量(最好不要take ,因為這是Prelude一個函數),從而限制數量。

您的代碼中的這種限制顯然是take $ sumList xs ,這很奇怪:在您的函數中take是一個Int ,而$基本上會將您的語句寫入(x + take) (sumList xs) 因此,您顯然希望使用(x + take) (一個Int )作為函數,並將sumList xs作為參數來執行函數應用程序。 但是Int不是函數,因此它不進行類型檢查,也不包含任何限制數字的邏輯。

所以基本上我們應該考慮三種情況:

  1. 在這種情況下總和為0的空列表;
  2. 要采用的元素數小於或等於零,在這種情況下,總和為0
  3. 要取的元素數大於0 ,在這種情況下,我們將頭部添加到從尾部少取一個元素的總和中。

所以一個簡單的映射是:

sumTakeList :: (Integral i, Num n) => i -> [n] -> n
sumTakeList _ [] = 0
sumTakeList t (x:xs) | t <= 0 = 0
                     | otherwise = x + sumTakeList (t-1) xs

但是你不需要自己寫這樣的邏輯,你可以結合take :: Int -> [a] -> [a]內置函數sum :: Num a => [a] -> a函數:

sumTakeList  :: Num n => Int -> [n] -> n
sumTakeList t = sum . take t

現在,如果您需要對前五個元素求和,我們可以將其設為特例:

subList5 :: Num n => [n] -> n
sumList5 = sumTakeList 5

Hoogle 是查看哪些功能可用以及它們如何工作的絕佳資源。 這里是它的網頁上take和文檔你想要的功能

如您所見,名稱take已被采用,但您可以使用它來實現此功能。

請注意,您的sumList需要另一個參數,即要求和的元素數。 你想要的語法是這樣的:

sumList :: Int -> [Int] -> Int
sumList n xs = _ $ take n xs

_是空白的地方,您可以自己填寫。 它是 Prelude 中的一個函數,但類型簽名有點太復雜,現在無法進入。

或者您可以遞歸地編寫它,使用兩個基本情況和第三個累加參數(通過輔助函數):

sumList :: Int -> [Int] -> Int
sumList n xs = sumList' n xs 0 where
  sumList' :: Int -> [Int] -> Int -> Int
  sumList' 0 _ a = _ -- A base case.
  sumList' _ [] a = _ -- The other base case.
  sumList' m (y:ys) a = sumList' _ _ _ -- The recursive case.

在這里,等號左邊的_符號應該保留在那里,這意味着模式保護忽略了該參數,但右邊的_符號是空白,供您自己填寫。 同樣,GHC 會告訴您填充孔所需的類型。

這種尾遞歸函數是 Haskell 中非常常見的模式; 您想確保每次遞歸調用都使您更接近基本情況。 通常,這意味着從計數參數中減去 1 來調用自身,或者將列表參數的尾部作為新的列表參數來調用自身。 在這里,您想同時進行。 當您遞歸調用函數本身時,不要忘記更新您的運行總和a

這是一個簡短但甜蜜的答案。 你真的很親近。 考慮以下:

  • take參數告訴您需要總結多少個元素,因此如果您執行sumList 0 anything您應該總是得到0因為您沒有使用任何元素。
  • 如果您想要前n元素,請將第一個元素添加到總數中並計算接下來的n-1元素的總和。

     sumList 0 anything = 0 sumList n [] = 0 sumList n (e:es) = e + sumList (n-1) e

暫無
暫無

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

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