26

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

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

这怎么能评价?似乎它只会进入无限循环或其他东西。我知道它计算为循环箭头组合器,但我也不明白它是如何工作的。

编辑:那个权力的例子真的很有帮助。但是,您将如何使用 do 表示法来编写它?我假设您需要使用rec。

4

2 回答 2

27

由于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
于 2011-03-23T13:33:10.113 回答
14

这是一个真实的例子:

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' 以产生 'length b',它返回到循环并绑定到 'd'。

在“循环”中,这个“d=length b”被输入到“f”中,用于drop中的计算。

这对于构建不可变双向链表(也可能是循环的)等技巧很有用。对于遍历 'b' 一次以生成一些解析的 'd'(例如长度或最大元素)并构建依赖于 'd' 的新结构 'c' 也很有用。懒惰避免了必须遍历'b'两次。

于 2011-03-23T20:46:12.033 回答