List 使用Haskell的交替级数
我试图使用Haskell读取列表并执行交替序列,因此从第一个元素开始,每隔一个元素进行相加,然后从第二个元素中每隔一个元素进行减去。例如[1,2,3,4]将是1-2+3-4=-2。我想我已经知道了如何对特定长度的列表执行此操作(我编写它是为了容纳空列表和最多4个元素长的列表),但它并没有达到我所认为的效果。它只返回列表中的第一个元素。以下是我所拥有的: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) =
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)
:)@Alecfoldr(-)0
似乎更简单(如果你不想严格要求的话)。@Davidletcher我只是在胡闹——我的评论不是认真的。虽然是的,但是使用foldl'
会更好。我喜欢这个,我喜欢foldl,但是Haskell是声明性的,所以altl ls=[如果偶数n那么(-n)否则n | n