计算具有相同长度的连续子列表

时间:2011-12-31 16:26:33

标签: haskell

我正在尝试计算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(前两个和后两个子列表具有相同的长度)。

有关问题的任何线索?

由于

4 个答案:

答案 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)