List 使用Haskell的交替级数

List 使用Haskell的交替级数,list,haskell,sum,List,Haskell,Sum,我试图使用Haskell读取列表并执行交替序列,因此从第一个元素开始,每隔一个元素进行相加,然后从第二个元素中每隔一个元素进行减去。例如[1,2,3,4]将是1-2+3-4=-2。我想我已经知道了如何对特定长度的列表执行此操作(我编写它是为了容纳空列表和最多4个元素长的列表),但它并没有达到我所认为的效果。它只返回列表中的第一个元素。以下是我所拥有的: altSeries :: Num a => [a] -> a altSeries [] = 0 altSeries (x:xs) =

我试图使用Haskell读取列表并执行交替序列,因此从第一个元素开始,每隔一个元素进行相加,然后从第二个元素中每隔一个元素进行减去。例如[1,2,3,4]将是1-2+3-4=-2。我想我已经知道了如何对特定长度的列表执行此操作(我编写它是为了容纳空列表和最多4个元素长的列表),但它并没有达到我所认为的效果。它只返回列表中的第一个元素。以下是我所拥有的:

altSeries :: Num a => [a] -> a
altSeries [] = 0
altSeries (x:xs) = if length xs == 1 then x
                   else if length xs == 2 then x
                   else if length xs == 3 then (x - xs!!1 + xs!!2)
                   else (x - xs!!1 + xs!!2 - xs!!3)

另外,如果我想使用任意大小的列表,该怎么办?

我想很明显,这个解决方案不可伸缩:即使你将函数写入长度为千的列表,它也会从有人输入一个包含千和一个元素的列表的那一刻起中断

列表处理通常是递归进行的:我们处理列表的头(或前k个头),并在列表的尾部执行递归。此外,我们有许多基本情况(可以是一个)来终止递归

您已经提供了一个基本情况:空列表:

altSeries [] = 0
如果我们输入一个空列表,很明显我们应该返回
0
。现在,我们可能会问,如果获得长度为1的列表,该怎么办:在这种情况下,列表的形状为
[x]
。因此,在这种情况下,我们应该返回
x
,因为我们应该添加
x
,而不能减去第二个数字。因此,我们补充说:

altSeries [x] = x
现在的问题是如何处理长度为两个或两个以上的列表。在这种情况下,列表具有模式
(x1:x2:t)
,其中
x1
为第一个元素,
x2
为第二个元素,
t
为剩余元素。根据您的描述,我们应计算
x1-x2
并添加
altSeries t

altSeries (x1:x2:t) = x1 - x2 + altSeries t
这是因为
altSeries
随后将递归提取
x3
x4
,因此它将递归到:

altSeries [x1,x2,x3,x4,x5] = x1 - x2 + altSeries [x3,x4,x5]
                           = x1 - x2 + x3 - x4 + altSeries [x5]
                           = x1 - x2 + x3 - x4 + x5
因此,全面实施是:

altSeries :: Num n => [n] -> n
altSeries [] = 0
altSeries [x] = x
altSeries (x1:x2:t) = x1 - x2 + altSeries t

您需要的是一个列表,它最终看起来像这样,您可以求和

您可以列出交替符号
[1,-1]
。使用函数可以使其无限大<代码>让交替信号=循环[1,-1]

要转换
[1,2,3,4]
列表,您可以将无限交替列表压缩为带有(*)交替信号输入的
zipWith

整个函数将如下所示:

altSeries :: Num a => [a] -> a
altSeries input = let alternatingSigns = cycle [1,-1]
                  in sum $ zipWith (*) alternatingSigns input

我不知道您是如何测试代码的,但它甚至不能处理长度为1的列表。对于大多数练习,您应该忘记
length,head,tail存在,并且纯粹使用模式匹配和递归。可以简化为
altSeries xs=sum(zipWith(*)(循环[1,-1])xs)
altSeries=sum。id为的zipWith(cycle[id,negate])
。复杂度很好。不需要比这更复杂或抽象。:)@melpomene:虽然这显然有效,但它引入了许多更高级别的函数和其他魔法。基于这个问题,我认为这不会提高OP的水平。但请随意添加一个答案:)我会投票。这些解决方案虽然正确,但不必要地复杂。试试这个:
altSeries(x:xs)=x-altSeries-xs
altSeries=0
这不仅简单,而且几乎肯定更快。当然,如果您想要高性能,您需要严格执行此函数:@CarlEdman Strictness不会影响该函数的速度。事实上,任何严格程度的提高都会减慢速度。如果您想要更好的性能,您将需要一个尾部递归函数,因此
altSeries=go 0 1其中go t[]=t;go t m(x:xs)=go(t+m*x)(否定m)xs
,现在您需要严格的数值参数,尽管GHC有希望为您解决这个问题。既然我们在做,为什么不取消乘法呢<代码>altSum=任一id。foldl(\e n->任意一个(右。(+n))(左。(减去n))e)(左0)
:)@Alec
foldr(-)0
似乎更简单(如果你不想严格要求的话)。@Davidletcher我只是在胡闹——我的评论不是认真的。虽然是的,但是使用
foldl'
会更好。我喜欢这个,我喜欢foldl,但是Haskell是声明性的,所以
altl ls=[如果偶数n那么(-n)否则n | n