So you know如何对列表中的数字求和,
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
?我们没有指定它,因此我们的函数的用户(即我们自己)很容易犯错误并使用不正确的初始值。顺便说一下,什么is正确的初始值?
好的,让我们这样做:
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
to n
).
sumUpToNElements :: Int -> [Int] -> Int
sumUpToNElements n xs = .......
........
Done.
多说一句忠告:不要使用$
在你刚开始学习 Haskell 的时候。使用explicit括号。
sumList take (x:xs) = x + take $ sumList xs
被解析为
sumList take (x:xs) = (x + take) (sumList xs)
这会将两个不相关的数字相加,然后将结果用作要调用的函数(sumList xs)
作为参数(换句话说,这是一个错误)。
如果您使用显式括号,您可能不会那样写。