Frage

Ich habe mit Lambda-Ausdrücke in Oxygene experimentiert. Sehr einfacher Ausdruck rekursive Lambda eine Fibonacci-Zahl zu berechnen:

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

Wenn ich diesen Code ausführen bekomme ich eine Nullreferenceexception. Alle Ideen, was ich falsch mache?

War es hilfreich?

Lösung

Sie tun nichts falsch gemacht. Wenn überhaupt, sollte der Compiler Sie warnen vor fib verwenden, eine freie Variable, im Inneren des Körpers des Lambda.

Allerdings sollte die Compiler fib als Standort zu erfassen, so dass, wenn die Zuordnung abgeschlossen ist und die Delegierten später aufgerufen wird, wird fib richtig zugewiesen und Rekursion sollte wie erwartet.

Der naheliegendste mögliche Grund für das Scheitern ist, dass Prism nicht erfassen Standorte, sondern Werte, die grob unintuitive und mit jeder anderen Verschluss Umsetzung in nicht-reinen Sprachen im Widerspruch sein würde.

Zum Beispiel, versuchen Sie diesen Code in JavaScript (im Gegensatz zu Craig der Behauptung in den Kommentaren zu diesem Beitrag JavaScript erfaßt auch Standorte, nicht Werte):

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

Die Alertbox nach dem Klicken auf die Schaltfläche zeigen 1 bzw. 2, während im Anschluss an Prism / Oxygene Semantik sie 1 beide Male zeigen würde.

Andere Tipps

Steve:

Das Problem hat sich offenbar in Delphi Prism 2010 Das folgende Codebeispiel arbeitet in der offiziellen Veröffentlichung angesprochen.

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

Die MessageBox zeigt den Wert 34.

Als Reaktion auf Jeroen Frage, dieser Code wurde in dem ursprünglichen, offiziellen Release-Build ausführen, 3.0.21.661.

als vorübergehende Lösung Sie verwenden können:

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 übernimmt die Erfassung von lokalen Variablen anders dann nativen Delphi oder C #. In diesen 2 alle Verweise im Code dieser Einheimischen werden auf Feldern der Compiler generierten Klasse zugeordnet werden, die Ihre anonyme Methode halten wird. In Prisma, bleiben diese Einheimischen gewöhnlichen Einheimischen, doch sind die Felder dieses verborgenen Felder gesetzt, wenn Sie die anonyme Methode instanziiert.

Eine Möglichkeit, eine rekursive Lambda zu bekommen, wäre einen Referenztyp zu verwenden, um das Lambda für Sie zu halten.

Das alles klingt viel komplizierter dann es wirklich ist.
2 Methoden zum Erreichen Sie Ihr Ziel:
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) Wenn Sie nicht möchten, einen Verweis auf diese Wrapper haben, können Sie es tun, wie so:


    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, der Grund für Prism nicht hier folgenden C # ist, dass zum Einfädeln und Schleife, diese Wiederverwendung von gefangenen Vars für harte seltsame Laufzeit Probleme macht. In Prism, werden erfaßt Aufnahmen wirklich, sobald Sie die anonyme Methode oder Lambda zuweisen. Die eine gewisse immuatble Note hat ...

Cheers, Robert

Gilt das auch für Anonyme Methoden anwenden? Ich vermute, es tut, kann aber durchaus nicht die Syntax herauszufinden, bekommen dies zu laufen

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

Bearbeiten

Es tut.

  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;

Ich habe versucht, die Zuordnung auch die Variable:

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

Immer noch ohne Glück.

Aus Neugier habe ich versucht, etwas ähnliches anonyme Methoden verwenden.

Im Prinzip implementiert ich eine Tiefensuche auf einem direkten azyklischen Graphen eine rekursive anonyme Methode:

var dfs : dfsmethod;
dfs := method(Vertex : IVertex)
begin
  var IsDone : Boolean;
  Visited[Vertex.Key] := True;
  aMethod(Vertex.Key, Vertex.Weight, var IsDone);  //PreVisit
  if IsDone then Exit;
  for each Successor in Vertex.Successors do
    if not Visited[Successor.Key] then
      dfs(Successor);
end;
dfs(InternalGetVertex(aStart));

Das zusammengestellt, aber ich habe den gleichen Fehler. Nullreferenceexception.

Ich habe auch versucht, um die Fibonacci als rekursive anonyme Methode neu zu implementieren:

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

wieder gleiches Problem! Immer auf der zweiten Iteration (das heißt ersten rekursiven Aufruf)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top