Delphi Prism/Oxygene 中的 Lambda 表达式
我一直在 Oxygene 中尝试 Lambda 表达式。 用于计算斐波那契数的非常简单的递归 lambda 表达式:
var fib : Func<int32, int32>;
fib := n -> iif(n > 1, fib(n - 1) + fib(n - 2), n);
fib(3);
当我运行此代码时,我得到一个 nullreferenceException。 关于我做错了什么有什么想法吗?
I have been experimenting with Lambda expressions in Oxygene. Very simple recursive lambda expression to calculate a fibonacci number :
var fib : Func<int32, int32>;
fib := n -> iif(n > 1, fib(n - 1) + fib(n - 2), n);
fib(3);
When I run this code I get a nullreferenceexception. Any ideas as to what I'm doing wrong?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
你没有做错任何事。 如果有的话,编译器应该警告您在 lambda 体内使用 fib(一个未分配的变量)。
然而,编译器应该将 fib 捕获为一个位置,以便当分配完成并且稍后调用委托时,fib 被正确分配并且递归应该按预期工作。
失败的最明显的可能原因是 Prism 捕获的不是位置,而是值,这非常不直观,并且与非纯语言中的所有其他闭包实现不一致。
例如,在 JavaScript 中尝试此代码(与 Craig 在本文评论中的断言相反,JavaScript 还捕获位置,而不是值):
单击按钮后的警报框分别显示 1 和 2,同时遵循 Prism/Oxygene 语义他们两次都会显示 1。
You aren't doing anything wrong. If anything, the compiler should warn you about using fib, an unassigned variable, inside the body of the lambda.
However the compiler ought to be capturing fib as a location, so that when the assignment completes and the delegate is later invoked, fib is properly assigned and recursion should work as expected.
The most obvious possible reason for the failure is that Prism isn't capturing locations, but values, which would be grossly unintuitive and at odds with every other closure implementation in non-pure languages.
For example, try this code in JavaScript (contrary to Craig's assertion in the comments to this post, JavaScript also captures locations, not values):
The alert boxes after you click on the button show 1 and 2 respectively, while following Prism/Oxygene semantics they would show 1 both times.
Steve:
这个问题显然已在 Delphi Prism 2010 中得到解决。以下代码示例在正式版本中有效。
MessageBox 显示值 34。
为了回答 Jeroen 的问题,此代码在原始的官方发布版本 3.0.21.661 中运行。
Steve:
The issue has apparently been addressed in Delphi Prism 2010. The following code sample works in the official release.
The MessageBox shows the value 34.
In response to Jeroen's question, this code was run in the original, official release build, 3.0.21.661.
作为临时解决方法,您可以使用:
as a temporary workaround you can use:
Prism 处理局部变量捕获的方式与本机 Delphi 或 C# 不同。
在这两个中,您的代码中对这些局部变量的所有引用都将映射到编译器生成的类的字段,该类将保存您的匿名方法。
在 prism 中,这些局部变量仍然是普通的局部变量,但这些隐藏字段的字段是在实例化匿名方法时设置的。
获得递归 lambda 的一种方法是使用引用类型来保存 lambda。
所有这些听起来比实际情况要复杂得多。
实现目标的 2 种方法:
1)
2)当你不想引用这个包装器时,你可以这样做:
顺便说一句,Prism 不遵循 C# 的原因是,对于线程和循环,这种捕获变量的重用使得很难奇怪的运行时问题。
在 Prism 中,捕获是在您分配匿名方法或 lambda 时真正捕获的。 这有一种不可改变的感觉……
干杯,
罗伯特
Prism handles the capture of local variables differently then native Delphi or C#.
In those 2 all references in your code of those locals will be mapped to fields of the compiler generated class that will hold your anonymous method.
In prism, these locals stay ordinary locals, yet the fields of this hidden fields are set when you instantiate the anonymous method.
One way to get a recursive lambda, would be to use a reference type to hold the lambda for you.
All of this sounds much more complicated then it really is.
2 methods of accomplishing your goal:
1)
2)When you do not want to have a reference to this wrapper, you can do it like so:
btw, the reason behind Prism not following C# here, is that for threading and loop, this reusing of captured vars makes for hard weird runtime problems.
In Prism, captures are really captured the moment you assign the anonymous method or lambda. Which has a certain immuatble touch to it...
Cheers,
Robert
这同样适用于匿名方法吗? 我猜它确实如此,但无法完全弄清楚让它运行的语法
编辑
它确实如此。
Does the same apply to Anonymous Methods? I'm guessing it does, but can't quite figure out the syntax to get this to run
Edit
It does.