2

我一直在尝试在 Oxygene 中使用 Lambda 表达式。非常简单的递归 lambda 表达式来计算斐波那契数:

var fib : Func<int32, int32>;
fib := n -> iif(n > 1, fib(n - 1) + fib(n - 2), n);
fib(3);

当我运行此代码时,我得到一个 nullreferenceexception。关于我做错了什么的任何想法?

4

5 回答 5

5

你没有做错什么。如果有的话,编译器应该警告你在 lambda 的主体内使用 fib,一个未分配的变量。

然而,编译器应该将 fib 捕获为一个位置,以便当分配完成并且稍后调用委托时,fib 被正确分配并且递归应该按预期工作。

失败的最明显可能原因是 Prism 不是在捕获位置,而是在捕获值,这非常不直观,并且与非纯语言中的所有其他闭包实现不一致。

例如,在 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>

单击按钮后的警报框分别显示 1 和 2,而遵循 Prism/Oxygene 语义它们将显示 1 两次。

于 2008-11-06T22:23:46.203 回答
4

史蒂夫:

该问题显然已在 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);

MessageBox 显示值 34。

在回答 Jeroen 的问题时,此代码在原始的官方发布版本 3.0.21.661 中运行。

于 2009-09-29T08:40:54.470 回答
1

作为临时解决方法,您可以使用:

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);
于 2008-11-07T13:20:10.170 回答
0

Prism 处理局部变量的捕获方式与原生 Delphi 或 C# 不同。在这两个本地代码中的所有引用都将映射到编译器生成的类的字段,该类将保存您的匿名方法。在 prism 中,这些 locals 仍然是普通的 locals,但是这个隐藏字段的字段是在您实例化匿名方法时设置的。

获得递归 lambda 的一种方法是使用引用类型为您保存 lambda。

所有这些听起来比实际上要复杂得多。
实现目标的 2 种方法:
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)当你不想引用这个包装器时,你可以这样做:


    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;

顺便说一句,Prism 在这里没有遵循 C# 的原因是,对于线程和循环,这种对捕获的变量的重用会导致非常奇怪的运行时问题。在 Prism 中,捕获在您分配匿名方法或 lambda 的那一刻真正被捕获。它具有某种不可改变的触感……

干杯,罗伯特

于 2008-11-07T11:57:15.237 回答
0

这同样适用于匿名方法吗?我猜是这样,但不能完全弄清楚让它运行的语法

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

编辑

确实如此。

  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;
于 2008-11-07T12:07:27.680 回答