支持本机递归

时间:2017-06-07 19:00:24

标签: haskell recursion

我读了一篇关于Haskell递归的文章说:

  

但是lambda演算并没有出现在表面上   由于表达式的匿名性,递归方式。怎么做   你打电话给没有名字的东西?能够写递归   但是,函数对于图灵完整性至关重要。我们使用了   组合器 - 称为Y组合器或定点组合器 -   在lambda演算中编写递归函数。 Haskell有本地人   基于与Y组合子相同的原理的递归能力。

原生递归是什么意思?

请考虑以下代码段:

applyTimes :: (Eq a, Num a) => a -> (b -> b) -> b -> b
applyTimes 0 f b = b
applyTimes n f b = f (applyTimes (n-1) f b)

上述代码不符合Y combinator原则,因为applyTimes在函数体本身中被调用,并且之前没有定义。
如果我错了,请证明我的答案。

3 个答案:

答案 0 :(得分:4)

"原生递归"意味着语言本身支持递归定义。与简单的lambda演算不同,所有术语都是匿名的,Haskell确实有一种命名表达式的方法;而且,在给出名称的定义时,您可以使用该名称本身。您自己观察到了这一点:在定义applyTimes时,您使用了名称applyTimes,因此利用了Haskell对递归的原生支持。

您还可以想象一种支持命名表达式而不是递归的不同语言;事实上,许多功能语言已经区分了" letrec"和"让"分别为递归且不递归的定义形式。在这种语言中,applyTimes的定义如果使用了" let"形式,如果使用" letrec"则接受形式。

答案 1 :(得分:2)

“Native recursion”只是意味着Haskell以递归定义和let绑定的形式将递归作为“本机”(内置)语言特性:

-- Recursive definition
map f (x : xs) = f x : map f xs
                       ---
map _ [] = []

-- Recursive “let” binding
main = let ones = 1 : ones in print (take 10 ones)
                      ----

在内部,编译器可以使用定点组合器(fix)重写这些,例如在类型检查之前进行简化,但我不知道它是否确实这样做了

map f xs = let
  map' k (x : xs) = f x : k xs
  map' _ [] = []
  in fix map' xs

main = let
  ones = fix ones'
  ones' k = 1 : k
  in print (take 10 ones)

在Haskell中,定点组合器是使用对递归绑定的本机支持实现的:

-- Naïve implementation
fix f = f (fix f)

-- Optimisation to improve sharing
fix f = let x = f x in x

任何递归函数都可以用fix表示:

applyTimes :: (Eq a, Num a) => a -> (b -> b) -> b -> b
applyTimes 0 f b = b
applyTimes n f b = f (applyTimes (n-1) f b)

applyTimes n f b = let
  applyTimes' _ 0 = b
  applyTimes' k n = f (k (n - 1))
  in fix applyTimes' n

注意没有原生递归,Y组合子\ f -> (\ x -> f (x x)) (\ x -> f (x x))不能用简单类型的lambda演算表示,因为x没有允许它的类型是一个以x为参数的函数。具体来说,它失败了“发生检查”:如果x是一个函数,那么它必须具有a -> b形式的类型,但如果x作为参数传递给{{1}那么它必须具有类型x。显然,a不能等于a,因为a -> b中出现了a,因此会无限扩展:a -> ba -> b(a -> b) -> b等等。

虽然Haskell的类型系统比STLC更强大,但是对于类型化的函数式语言来说,它通常提供递归作为语言的原始部分而不是定点组合器。

答案 2 :(得分:1)

  

上面的代码不符合fix :: (a -> a) -> a fix f = f (fix f) applyTimes :: (Eq a, Num a) => a -> (b -> b) -> b -> b applyTimes n f b = fix go n where go rec n = if n == 0 then b else f (rec (n - 1)) 原则,因为applyTimes在函数体本身中调用,之前没有定义过。

这样的事情怎么样?

{{1}}
相关问题