如何使用文件夹在列表中相互添加变量?

问题描述 投票:0回答:1

给出列表[x0,x1,x2,。 。 。 ,xn-1],函数应该返回列表[y0,y1,y2,。 。 。 ,yn-1],其中y0 = x0,y1 = x0 + x1

因此,如果将[1,2,3]作为输入,则将获得[1,3,6]作为输出

我不完全了解文件夹,所以也许我能在设法弄清楚如何更改最后一行以获得正确答案的过程中获得一些帮助。

scan :: [Integer] -> [Integer]
scan [] = []
scan [x] = [x]

scan (x:xs) = x : foldr (/y -> y (+) x) 0 (scan xs)

我的初始解决方案(有效)使用地图功能。

scan :: [Integer] -> [Integer]
scan [] = []
scan [x] = [x]

scan (x:xs) = x : map (+x) (scan xs)

list function haskell types fold
1个回答
1
投票

EDIT,我添加了第一部分以更好地解决您的两个实现。

首先,使用foldr解决您的实现问题,以下是几点说明:

  1. Lambda在Haskell中以反斜杠开头,而不是斜杠。这是因为反斜杠看起来像lambda希腊字母(λ)。
  2. 仅使用特殊字符命名的功能,例如+,在默认情况下为infix。如果在它们周围使用括号,它将变成前缀函数:

$> (+) 1 5 $> 6

    传递给foldr的函数有两个参数,而您在lambda中只提供了一个。如果您确实想忽略第二个,可以使用_而不是将其绑定到变量(\x _ -> x)。
  • 我认为,通过此实现,您将陷入困境。请参阅以下讨论,以了解我对解决此问题的正确方法。

    Note:可以使用mapfoldr)实现source,这是在工作(第二个)实现中使用foldr的一种方式。


    foldr实现不是最佳方法,因为顾名思义,它从右边折叠起来:

    foldr1 (+) [1..5] --is equivalent to: (1+(2+(3+(4+5))))

    如您所见,求和操作是从列表的末尾开始的,这不是您想要的。为了使这项工作有效,您必须“作弊”,并将列表翻转两次,一次折叠之前,一次折叠之后:

    scan = tail . reverse . foldr step [0] . reverse where step e acc@(a:_) = (e + a) : acc

    您可以使用左折,从左折向更好:

    foldl1 (+) [1..5] --is equivalent to: ((((1+2)+3)+4)+5)

    但是,这仍然不是理想的,因为要保持累加器中元素的顺序相同,就必须使用++函数,该函数的二次时间复杂度很高。一种折衷方法是使用:函数,但是折叠之后您仍然必须反转累加器列表,这只是线性复杂度:

    scan' :: [Integer] -> [Integer] scan' = tail . reverse . foldl step [0] where step acc@(a:_) e = (e + a) : acc

    这仍然不是很好,因为reverse添加了额外的计算。因此,理想的解决方案是使用scanl1,作为奖励,它不需要您提供起始值(在上述示例中为[0]):

    scan'' :: [Integer] -> [Integer] scan'' = scanl1 (+)

    [scanl1是根据scanl1来实现的,其大致定义如下:

    scanl

    因此,您可以简单地做:

    scanl f init list = init : (case list of [] -> [] x:xs -> scanl f (f init x) xs)

    最后,您的$> scanl1 (+) [1..3]
    $> [1,3,6]
    函数不必要专门用于scan,因为它只需要Integer约束:

    Num

    这甚至可能会提高性能,但这就是我能力的终结,所以我不会再继续了:)
  • © www.soinside.com 2019 - 2024. All rights reserved.