如何表达function iteration是否有简洁,惯用的方式?也就是说,给定一个数字n
和一个函数f :: a -> a
,我想表达\x -> f(...(f(x))...)
应用f
的地方n
- 次。
当然,我可以为此制作自己的递归函数,但如果有一种方法可以使用现有工具或库快速表达它,我会感兴趣。
到目前为止,我有这些想法:
\n f x -> foldr (const f) x [1..n]
\n -> appEndo . mconcat . replicate n . Endo
但它们都使用中间列表,并且不是很简洁。
到目前为止我找到的最短的一个使用半群:
\n f -> appEndo . times1p (n - 1) . Endo
,但它仅适用于正数(不适用于0)。
主要是我专注于Haskell中的解决方案,但我也对Scala解决方案甚至其他功能语言感兴趣。
答案 0 :(得分:23)
因为Haskell受数学影响太大,所以你链接到的维基百科页面中的the definition几乎直接翻译成语言。
请检查一下:
现在在Haskell:
iterateF 0 _ = id
iterateF n f = f . iterateF (n - 1) f
非常整洁,呵呵?
这是什么?这是一种典型的递归模式。 Haskellers通常如何对待它?我们用折叠来对待它!因此,在重构后,我们最终得到以下翻译:
iterateF :: Int -> (a -> a) -> (a -> a)
iterateF n f = foldr (.) id (replicate n f)
或点免费,如果您愿意:
iterateF :: Int -> (a -> a) -> (a -> a)
iterateF n = foldr (.) id . replicate n
如您所见,维基百科定义和此处提供的解决方案中都没有主题函数参数的概念。它是另一个函数的函数,即主题函数被视为一个值。这是一个问题的高级方法,而不是涉及主题函数的参数的实现。
现在,关于你对中间名单的担忧。从源代码的角度来看,这个解决方案与@jmcejuela发布的Scala解决方案非常相似,但GHC优化器完全抛弃中间列表有一个关键的区别,将函数转换为主题函数的简单递归循环。我不认为它可以更好地进行优化。
为了自己轻松地检查中间编译器结果,我建议使用ghc-core。
答案 1 :(得分:15)
答案 2 :(得分:1)
虽然这不像jmcejuela的答案那样简洁(我更喜欢),但是在scala中还有另一种方法可以在没有Function
模块的情况下表达这样的函数。它在n = 0时也有效。
def iterate[T](f: T=>T, n: Int) = (x: T) => (1 to n).foldLeft(x)((res, n) => f(res))
为了克服列表的创建,可以使用显式递归,反过来需要更多静态类型。
def iterate[T](f: T=>T, n: Int): T=>T = (x: T) => (if(n == 0) x else iterate(f, n-1)(f(x)))
有一种使用模式匹配的等效解决方案,如Haskell中的解决方案:
def iterate[T](f: T=>T, n: Int): T=>T = (x: T) => n match {
case 0 => x
case _ => iterate(f, n-1)(f(x))
}
最后,我更喜欢在Caml中编写它的简短方法,根本不需要定义变量的类型。
let iterate f n x = match n with 0->x | n->iterate f (n-1) x;;
let f5 = iterate f 5 in ...
答案 3 :(得分:1)
我最喜欢pigworker / tauli的想法,但由于他们只是作为评论给出了它,我正在做出CW答案。
\n f x -> iterate f x !! n
或
\n f -> (!! n) . iterate f
甚至可能是:
\n -> ((!! n) .) . iterate