如何简洁地表达函数迭代?

时间:2013-04-15 08:29:51

标签: scala haskell functional-programming iteration

如何表达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解决方案甚至其他功能语言感兴趣。

4 个答案:

答案 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)

在Scala中:

Function chain Seq.fill(n)(f)

scaladoc for Function。懒惰版本:Function chain Stream.fill(n)(f)

答案 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