2011-03-23 34 views
22

在箭头符号中,可以使用rec关键字来编写递归定义。例如:Haskell rec关键字如何工作?

rec 
    name <- function -< input 
    input <- otherFunction -< name 

这怎么能评估?它似乎会进入一个无限循环或什么东西。我知道它评估到循环箭头组合器,但我不明白这是如何工作。

编辑:这个权力的例子真的很有帮助。尽管如此,你会如何写这个符号?我假设你需要使用rec。

+4

[案例](http://www.haskell.org/haskellwiki/Keywords#rec)之前有人没有听说过“rec”。 – 2012-10-19 13:00:50

回答

22

由于haskells懒惰,这一点神奇的作品。正如您可能知道的那样,Haskell在需要时评估一个值,而不是在定义时评估。因此,如果您不需要直接输入的值,或者稍后输入值,则此功能可以正常工作。

rec使用loop函数ArrowLoop实现。这是其次的定义:

class Arrow a => ArrowLoop a where 
     loop :: a (b,d) (c,d) -> a b c 

instance ArrowLoop (->) where 
     loop f b = let (c,d) = f (b,d) in c 

你可以看到:输出只是反馈作为输入。它只会计算一次,因为Haskell在需要时只会评估d

下面是如何直接使用loop组合子的实际示例。此函数计算它的参数的所有权力:

powers = loop $ \(x,l) -> (l,x:map(*x)l) 

(你也可以写像这样代替:powers x = fix $ (x :) . map (*x)

它是如何工作的?那么,权力的无限列表是在l的论点。评价是这样的:

powers = loop $ \(x,l) -> (l,x:map(*x)l) ==> 
powers b = let (c,d) = (\(x,l) -> (l,x:map(*x)l)) (b,d) in c ==> 
powers b = let (c,d) = (d,b:map(*b)d) in d ==> -- Now we apply 2 as an argument 
powers 2 = let (c,d) = (d,2:map(*2)d) in d ==> 
     = let (c,(2:d)) = (d,2:map(*2)d) in c ==> 
     = let (c,(2:4:d)) = ((2:d),2:map(*2)(2:d)) in c ==> 
     = let (c,(2:4:8:d)) = ((2:4:d),2:map(*2)(2:4:d)) in ==> -- and so on 
+0

我明白,但是它从哪里得到第一个输出样本?是否有基地案件定义的地方,因为我没有看到。如果我的示例中没有提供输入或名称,那么如何评估?还是必须提供其中的一个? – 2011-03-23 13:58:53

+1

诀窍是,Haskell不会评估未使用的参数。只是控制流程没有必要与您想象的一样。 – fuz 2011-03-23 15:20:42

+1

'f(b,_)'在计算输出'd'时只能使用输入'b'。 'f'在构建'c'时可以同时使用'b'和'd',只要它不强迫评估'd'使其挂起。你可以这样想:当'c'被强制时,'d'从'b'计算,然后'c'从'b'和'd'计算。 – 2011-03-23 20:50:44

11

这里是一个真正的十岁上下的例子:

loop f b = let (c,d) = f (b,d) in c 

f (b,d) = (drop (d-2) b, length b) 

main = print (loop f "Hello World") 

该程序输出 “LD”。函数'loop f'需要一个输入'b'并创建一个输出'c'。 'f'正在做的是研究'b'来产生'长度b',这个长度被返回到循环并绑定到'd'。

在'循环'中,'d =长度b'被输入到'f'中,并在drop中用于计算。

这对于像构建一个不可变的双向链表(这也可能是循环)这样的技巧很有用。它也可用于遍历'b',既产生一些分析'd'(如长度或最大元素),又构建一个依赖于'd'的新结构'c'。懒惰避免了必须遍历'b'两次。