Haskell rec 关键字如何工作?

发布于 2024-10-25 21:40:31 字数 269 浏览 0 评论 0原文

在箭头do表示法中,您可以使用rec关键字来编写递归定义。举例来说:

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

这如何评估?看起来它会进入无限循环或其他什么。我知道它的计算结果为循环箭头组合器,但我也不明白它是如何工作的。

编辑:那个权力的例子真的很有帮助。不过,你会如何用 do 符号来写它呢?我假设你需要使用rec.

In arrow do notation, you can use the rec keyword to write recursive definitions. So for example:

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

How can this ever evaluate? It seems like it would just go into an infinite loop or something. I know it evaluates to the loop arrow combinator, but I don't understand how that works either.

EDIT: that powers example is really helpful. How would you write that with do notation, though? I assume you would need to use rec.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冷心人i 2024-11-01 21:40:31

这种神奇的效果是由于 haskells 的懒惰造成的。您可能知道,Haskell 在需要时计算值,而不是在定义时计算值。因此,如果您不需要直接输入值或稍后输入值,则此方法有效。

rec是使用ArrowLooploop函数实现的。其定义如下:

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

This bit of magic works due to haskells laziness. As you might know, Haskell evaluates a value when needed, not when defined. Thus, this works if you don't need the value fed in directly, or maybe later.

rec is implemented using the loop function of ArrowLoop. It is defined as followed:

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

You can see: The output is just fed back as the input. It will be calculated just once, because Haskell will only evaluate d when it's needed.

Here's an actual example of how to use the loop combinator directly. This function calculates all the powers of it's argument:

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

(You could also write it like this instead: powers x = fix $ (x :) . map (*x))

How does it works? Well, the infinite list of powers is in the l argument. The evaluation looks like this:

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
乜一 2024-11-01 21:40:31

这是一个真实的例子:

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”中,在“f”中用于drop的计算。

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

Here is a real-ish example:

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")

This program outputs "ld". The function 'loop f' takes one input 'b' and creates one output 'c'. What 'f' is doing is studying 'b' to produce 'length b' which is getting returned to loop and bound to 'd'.

In 'loop' this 'd=length b' is fed into the 'f' where it is used in the calculation in drop.

This is useful for tricks like building an immutable doubly linked list (which may also be circular). It is also useful for traversing 'b' once to both produce some analytic 'd' (such as length or biggest element) and to build a new structure 'c' that depends on 'd'. The laziness avoids having to traverse 'b' twice.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文