简体   繁体   English

Delphi Prism / Oxygene中的Lambda表达式

[英]Lambda Expressions in Delphi Prism/Oxygene

I have been experimenting with Lambda expressions in Oxygene. 我一直在试验Oxygene中的Lambda表达式。 Very simple recursive lambda expression to calculate a fibonacci number : 非常简单的递归lambda表达式来计算斐波纳契数:

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. 当我运行此代码时,我得到一个nullreferenceexception。 Any ideas as to what I'm doing wrong? 关于我做错了什么的任何想法?

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. 如果有的话,编译器应该警告你在lambda体内使用fib,一个未赋值的变量。

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. 但是编译器应该将fib作为一个位置捕获,这样当赋值完成并稍后调用委托时,就会正确分配fib并且递归应该按预期工作。

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. 失败的最明显的可能原因是Prism不是捕获位置,而是捕获值,这些非常不直观,并且与非纯语言中的每个其他闭包实现不一致。

For example, try this code in JavaScript (contrary to Craig's assertion in the comments to this post, JavaScript also captures locations, not values): 例如,在JavaScript中尝试此代码(与本文评论中的Craig断言相反,JavaScript也捕获位置,而不是值):

<html>
<head>
<script language='javascript'>
function main()
{
    var x = 1;
    var f = function() { return x; };
    alert(f());
    x = 2;
    alert(f());
}
</script>
</head>
<body>
<input type=button onclick="javascript:main()"></input>
</body>
</html>

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. 单击按钮后的警告框分别显示1和2,而按照Prism / Oxygene语义,它们将同时显示1。

Steve: 史蒂夫:

The issue has apparently been addressed in Delphi Prism 2010. The following code sample works in the official release. 这个问题显然已在Delphi Prism 2010中得到解决。以下代码示例在官方发布中有效。

 var fib : Func<int32, int32>;
 fib := n -> iif(n > 1, fib(n - 1) + fib(n - 2), n);
 var i := fib(9); //1,1,2,3,5,8,13,21,34
 MessageBox.Show(i.ToString);

The MessageBox shows the value 34. MessageBox显示值34。

In response to Jeroen's question, this code was run in the original, official release build, 3.0.21.661. 针对Jeroen的问题,此代码在原始的官方发布版本3.0.21.661中运行。

as a temporary workaround you can use: 作为临时解决方法,您可以使用:

var f := new class(f: Tfib := nil);
f.f := method(n : Int32): Int32
begin
  if n > 1 then  
    Result := f.f(n-1) + f.f(n-2)
  else
    Result := n;
end;
f.f(3);

Prism handles the capture of local variables differently then native Delphi or C#. Prism以不同于本机Delphi或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. 获取递归lambda的一种方法是使用引用类型为您保存lambda。

All of this sounds much more complicated then it really is. 所有这些听起来都比实际要复杂得多。
2 methods of accomplishing your goal: 2种实现目标的方法:
1) 1)


    var fib := new class(Call : Func<Integer, Integer> := nil);  
    fib.Call := n -> iif(n > 1, fib.Call(n - 1) + fib.Call(n - 2), n);  
    var x := fib.Call(3);  

2)When you do not want to have a reference to this wrapper, you can do it like so: 2)当你不想引用这个包装器时,你可以这样做:


    var fib : Func;  
    with fibWrapper := new class(Call : Func<Integer, Integer> := nil) do  
    begin  
        fibWrapper.Call := n -> iif(n > 1, fibWrapper.Call(n - 1) + fibWrapper.Call(n - 2), n);  
        fib := fibWrapper.Call;  
    end;

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. 顺便说一下,Prism不遵循C#的原因在于,对于线程和循环,重用捕获的变量会导致难以运行的奇怪的运行时问题。 In Prism, captures are really captured the moment you assign the anonymous method or lambda. 在Prism中,捕获是在分配匿名方法或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 我猜它确实如此,但无法弄清楚要运行它的语法

  var f : Tfib;
  f := method(n : Int32): Int32
  begin
    if n > 1 then  
      Result := f(n-1) + f(n-2)
    else
      Result := n;
  end;

Edit 编辑

It does. 确实如此。

  var f := new class(call : TFib := nil);
  f.call := method(n : Int32): Int32
  begin
    if n > 1 then  
      Result := f.call(n-1) + f.call(n-2)
    else
      Result := n;
  end;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM