转换Haskell中的2元组列表

时间:2014-04-03 05:03:00

标签: haskell

(背景:尝试学习Haskell,对函数式编程很新。通常用于Python。)

假设我有一个2元组的列表,直方图:

let h = [(1,2),(3,5),(4,6),(5,3),(6,7),(7,4),(8,6),(9,1)]

在命令性的术语中,我想将每对中的第二项更改为所有先前第二对的总和。在Python中,以下(公认的复杂)列表理解可以做到:

[(p[0], sum( [p[1] for p in histogram[:i+1]] ))
    for i, p in enumerate(histogram)]

假设histogram引用了上面h之类的2元组列表。

这是我到目前为止在Haskell中所拥有的:

zip [fst p | p <- h] (scanl1 (+) [snd k | k <- h])

这有效,但我想知道:

  • 这是通过列表读取一次还是两次?
  • 能表达得更好吗? (我希望如此。)

如果不清楚,这是上述的预期输出:

[(1,2),(3,7),(4,13),(5,16),(6,23),(7,27),(8,33),(9,34)]

3 个答案:

答案 0 :(得分:11)

您可以使用此功能

accumulate = scanl1 step
        where step (_,acc) (p1,p2) = (p1,acc+p2)

以下是您的示例数据的结果:

*Main> accumulate h
[(1,2),(3,7),(4,13),(5,16),(6,23),(7,27),(8,33),(9,34)]

答案 1 :(得分:5)

如果你是Haskell的新手,这可能有点太早了,但lens提供了一个很简洁的方法:

> scanl1Of (traverse . _2) (+) h
[(1,2),(3,7),(4,13),(5,16),(6,23),(7,27),(8,33),(9,34)]

您可以通过切换到_1

轻松累积第一个
> scanl1Of (traverse . _1) (+) h
[(1,2),(4,5),(8,6),(13,3),(19,7),(26,4),(34,6),(43,1)]

或者将所有值累积为一种嵌套列表:

> scanl1Of (traverse . both) (+) h
[(1,3),(6,11),(15,21),(26,29),(35,42),(49,53),(61,67),(76,77)]

答案 2 :(得分:4)

嗯,...... (,)Data.BifunctorData.Biapplicative

scanl1 (biliftA2 (flip const) (+))

是你想要的。


Functorf类型,任何a都可以将任何函数a->b应用于f a以获取f b。例如,(a,)Functor:有一种方法可以应用任何函数b->c(a,b)翻译为(a,c)

fmap f (x,y) = (x,f y)

Bifunctorf类型,任何ab都可以将a->cb->d两个函数应用于{{} 1}}获取f a b。例如,f c d(,):有一种方法可以应用任意一对函数Bifunctora->cb->d翻译成(a,b)

(c,d)

bimap f g (x,y) = (f x, g y) Biapplicative这样一种类型,fa可以将b应用于f (a->c) (b->d)以获取f a b }}。例如,f c d(,):有一种方法可以应用对中的任何函数将Biapplicative转换为(a,b)

(c,d)

biap (f,g) (x,y) = (f x, g y) Data.Biapplicative定义为&#34;提升&#34;一对函数biliftA2a->c->e - 构造一个类型为b->d->f(a,b)的两个参数的函数

(c,d)

所以biliftA2 f g = \(x,y) (z,t) -> (f x z, g y t) 构造了一个可以在biliftA2中使用的函数来进行必要的折叠。 scanl1将忽略前一对的第一个投影,flip const将加上前一对和下一对的第二个投影。