在箭头符号中,您可以使用 rec 关键字来编写递归定义。例如:
rec
name <- function -< input
input <- otherFunction -< name
这怎么能评价?似乎它只会进入无限循环或其他东西。我知道它计算为循环箭头组合器,但我也不明白它是如何工作的。
编辑:那个权力的例子真的很有帮助。但是,您将如何使用 do 表示法来编写它?我假设您需要使用rec。
由于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
这是一个真实的例子:
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'两次。