Question

J'ai expérimenté les expressions Lambda dans Oxygene. Expression lambda récursive très simple pour calculer un nombre de fibonacci:

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

Lorsque je lance ce code, je reçois une exception nullreference. Des idées sur ce que je fais mal?

Était-ce utile?

La solution

Vous ne faites rien de mal. Si tel est le cas, le compilateur devrait vous prévenir de l’utilisation de fib, une variable non affectée, dans le corps du lambda.

Cependant, le compilateur doit capturer fib en tant qu’emplacement. Ainsi, lorsque l’affectation est terminée et que le délégué est appelé ultérieurement, fib est correctement attribué et que la récursivité fonctionne correctement.

La raison la plus évidente de l’échec est que Prism ne capture pas d’emplacement, mais des valeurs, ce qui serait très peu intuitif et incompatible avec toute autre implémentation de clôture dans des langues non pures.

Par exemple, essayez ce code en JavaScript (contrairement à l’affirmation de Craig dans les commentaires de ce message, JavaScript capture également des emplacements, pas des valeurs):

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

Les boîtes d’alerte après avoir cliqué sur le bouton affichent respectivement 1 et 2, tandis qu’après la sémantique Prism / Oxygene, elles afficheraient 1 les deux fois.

Autres conseils

Steve:

Le problème a apparemment été résolu dans Delphi Prism 2010. L'exemple de code suivant fonctionne dans la version officielle.

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

La MessageBox affiche la valeur 34.

En réponse à la question de Jeroen, ce code a été exécuté dans la version d'origine, la version officielle, 3.0.21.661.

vous pouvez utiliser une solution temporaire:

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 gère la capture des variables locales différemment de Delphi ou C # natif. Dans ces 2, toutes les références dans votre code de ces locales seront mappées aux champs de la classe générée par le compilateur qui contiendra votre méthode anonyme. Dans le prisme, ces sections locales restent des sections locales ordinaires. Toutefois, les champs de ces champs masqués sont définis lorsque vous instanciez la méthode anonyme.

Une façon d'obtenir un lambda récursif serait d'utiliser un type de référence pour contenir le lambda à votre place.

Tout cela semble beaucoup plus compliqué qu’il ne l’est vraiment.
2 méthodes pour atteindre votre objectif:
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) Lorsque vous ne souhaitez pas faire référence à ce wrapper, vous pouvez le faire comme suit:


    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, la raison pour laquelle Prism ne suit pas C # ici, c’est que, pour les threads et les boucles, cette réutilisation des vars capturés pose de sérieux problèmes d’exécution. Dans Prism, les captures sont réellement capturées au moment où vous assignez la méthode anonyme ou lambda. Ce qui a une certaine touche immuatble à elle ...

Salut, Robert

La même chose s'applique-t-elle aux méthodes anonymes? J'imagine que c'est le cas, mais je ne parviens pas à comprendre la syntaxe pour que cela fonctionne

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

Modifier

C'est le cas.

  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;

J'ai aussi essayé d'attribuer la variable:

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

Toujours sans chance.

Par curiosité, j'ai essayé quelque chose de similaire avec des méthodes anonymes.

En gros, j'ai mis en œuvre une première recherche de profondeur sur un graphe acyclique direct à l'aide d'une méthode anonyme récursive:

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

Ceci compilé, mais j'ai eu la même erreur. NullReferenceException.

J'ai également essayé de réimplémenter Fibonacci en tant que méthode anonyme récursive:

  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)

encore le même problème! Toujours à la deuxième itération (c'est-à-dire premier appel récursif)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top