我正在尝试计算n和n + 1长度相同的列表元素。
我写了这段代码:
countSec :: [[Integer]] -> Integer
countSec (x:xs) = if (length x)==(length (head xs))
then 1+(countSec xs)
else (countSec xs)
countSec [] = 0
正如您可能猜到的那样,它无法正常工作。事实上,作为输出我得到" * 异常:Prelude.head:空列表"
countSec [[1,2][1,2],[2],[3,5],[2],[5]]
应该返回2(前两个和后两个子列表具有相同的长度)。
有关问题的任何线索?
由于
答案 0 :(得分:6)
您不会处理列表列表中只有一个元素的情况,因此只要您在head
上调用xs
,就会遇到错误,这将是{{ 1}},因为没有空列表的第一个元素:)
您可以使用模式匹配来解决此问题:
[]
我使用模式匹配来一次匹配列表中的两个元素。这就像countSec :: [[Integer]] -> Integer
countSec [] = 0
countSec [_] = 0
countSec (x : xs@(y:_))
| length x == length y = 1 + countSec xs
| otherwise = countSec xs
(模式可以嵌套),除了我们还将名称x:(y:_)
赋予我们与xs
匹配的内容;所以我们有
y:_
xs
x y _____\_____
\ \ \
[1] : ([2, 3] : ...)
是列表的第一个元素,x
是第二个元素,y
是第一个元素之后的列表。我们可以匹配xs
而使用(x:y:xs)
来递归,但这会占用更多内存并且更容易出错。
模式中的countSec (y:xs)
意味着我们不关心该位置的值,因此不想给它起一个名字;它是一个匹配所有内容的通配符。
作为一个次要的风格笔记,我也将你的_
表达转换成了一个守护者;这些基本上只是函数子句级别的表达式。
当模式匹配因为这样的事情而起作用时,你应该避免使用像if
那样的部分 1 函数 - 它们会隐藏错误并使代码更难以阅读。我还建议给GHC提供head
标志;如果你像我一样写了-Wall
但忘了零或单元素的情况,GHC会警告你:
countSec
1 未为其所有输入定义的函数;例如,Warning: Pattern match(es) are non-exhaustive
In an equation for `countSec': Patterns not matched: [_]
和head
未在tail
上定义,并且除数为0时未定义除法。
答案 1 :(得分:3)
一种不同的方法,只计算每个列表的长度一次,
countSec :: [[a]] -> Integer
countSec xss = genericLength . filter id $ zipWith (==) ls (tail ls)
where
ls = map length xss
部分函数tail
的使用在这里是完全安全的,因为zipWith
的快捷方式可以捕获它会引发错误的情况。
答案 2 :(得分:2)
列表的尾部,当您到达列表末尾时,xs
将为[]
,head
将导致错误。
然而,这类问题的一般答案是“从不使用head
。使用模式匹配”。这将立即显示程序中的错误:
countSec :: [[Integer]] -> Integer
countSec (x:y:xs) = if (length x)==(length y)
then 1+(countSec (y:xs))
else (countSec (y:xs))
countSec (x:[]) = ??
countSec [] = 0
答案 3 :(得分:1)
您可以使用一些内置版本:
import Data.Function (on)
import Data.List (groupBy)
countSec = length . head . groupBy ((==) `on` length)