【问题标题】:apply a function n times to the n-th item in a list in haskell将函数应用于haskell列表中的第n个项目n次
【发布时间】:2021-12-08 11:43:23
【问题描述】:

我想要一个高阶函数 g,它将另一个函数 f 应用于整数列表,使得

g = [f x1, f(f x2), f(f(f x3)), … , f^n(xn)]

我知道我可以映射类似的函数

g :: (Int -> Int) -> [Int] -> [Int]
g f xs = map f xs

我也可以应用一个函数 n 次

g f xs = [iterate f x !! n | x <- xs]

其中 n 应用函数的次数。我知道我需要使用递归,所以我认为这两个选项都没有用。

预期输出:

g (+1) [1,2,3,4,5] = [2,4,6,8,10]

【问题讨论】:

  • 我不禁觉得[x0, f x1, f (f x2), ...]——即到处少一个f——会是一种更自然的模式。
  • @DanielWagner,两者都可以,但不要同时使用scanl-style。
  • @dfeuer 哦,你的意思是我不是唯一一个觉得scanl 总是比我想要的多一个元素在前面?
  • @DanielWagner,不。 mapAccumL 说对了,但它在退出的过程中破坏了融合。

标签: haskell recursion higher-order-functions


【解决方案1】:

您可以使用显式递归,每次都传递要应用的函数和列表的尾部,因此:

g :: (Int -> Int) -> [Int] -> [Int]
g f = go f
    where go _ [] = []
          go fi (x:xs) = … : go (f . fi) xs

我在这里将实现… 部分作为练习。

另一个选项是使用两个列表,一个函数列表和一个值列表。在这种情况下,函数列表是iterate (f .) f:可以应用的函数的无限列表。然后我们可以将g 实现为:

g :: (Int -> Int) -> [Int] -> [Int]
g f = zipWith ($) (iterate (f .) f)

【讨论】:

  • 感谢您的帮助。我有 go fi (x:xs) = f x : g (f . fi) xs 之类的东西,但我得到的是 g (+1) [1,2,3,4,5] = [2,4,7,12,21] 而不是 [2,4,6,8,10] 它看起来像是以 2 的幂上升的。
  • @jamie:我打错了,是: go (f . fi) xs,不是: g (f . fi) xs
  • 现在我得到了g (+1) [1,2,3,4,5] = [2,3,4,5,6],这似乎是一个映射。我对go fi (x:xs) = f x 做错了吗?您的第二个实施有效。
  • @jamie:你搞错了,不是f x。
  • 我明白了!谢谢
【解决方案2】:

听起来像是 foldr 的另一种用法:

applyAsDeep :: (a -> a) -> [a] -> [a]
applyAsDeep f = foldr (\x xs -> f x : map f xs) []

λ> applyAsDeep (+10) [1,2,3,4,5]
[11,22,33,44,55]

【讨论】:

  • 这看起来不错,但实际上效率很低。你能看出为什么吗?您可以在仍然使用foldr 的同时修复它,但它的结构需要不同。
  • 一遍又一遍地映射?
  • 是的。结果中的每个(:) 构造函数的生成时间都比最后一个要长。
  • 是的,我需要把复杂性放在笔和纸上。之后再回来。
【解决方案3】:

如果你想有点矫枉过正......

import GHC.Exts (build)

g :: (a -> a) -> [a] -> [a]
g f xs0 = 
  build $ \c n -> 
    let go x r fi = fi x `c` r (f . fi) 
    in foldr go (const n) xs0 f

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-25
  • 1970-01-01
  • 2014-11-20
  • 1970-01-01
  • 1970-01-01
  • 2015-09-22
相关资源
最近更新 更多