Pergunta

Tenho feito experiências com expressões lambda em Oxygene. Muito simples expressão lambda recursiva para calcular um número de Fibonacci:

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

Quando eu executar esse código eu recebo um NullReferenceException. Todas as ideias sobre o que estou fazendo de errado?

Foi útil?

Solução

Você não está fazendo nada de errado. Se qualquer coisa, o compilador deve avisá-lo sobre o uso de lorota, uma variável não atribuída, dentro do corpo do lambda.

No entanto, o compilador deve ser capturar FIB como um local, de modo que quando os concluída atribuição e o delegado é mais tarde chamado, FIB está atribuído corretamente e recursão deve funcionar como esperado.

A possível razão óbvia mais para a falha é que Prism não está capturando locais, mas os valores, o que seria extremamente intuitiva e em desacordo com qualquer outra aplicação de fechamento em línguas não-puras.

Por exemplo, tente este código em JavaScript (contrariamente à afirmação de Craig nos comentários a este post, JavaScript também captura locais, e não valores):

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

As caixas de alerta após clicar no botão Mostrar 1 e 2, respectivamente, enquanto seguindo a semântica Prism / Oxygene eles mostrariam 1 duas vezes.

Outras dicas

Steve:

A questão tem sido aparentemente abordados em Delphi Prism 2010. O exemplo de código a seguir funciona na versão oficial.

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

Os shows MessageBox o valor 34.

Em resposta à pergunta de Jeroen, este código foi executado no original, compilação de lançamento oficial, 3.0.21.661.

como uma solução temporária, você pode usar:

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 lida com a captura de variáveis ??locais diferente, então nativo Delphi ou C #. Naqueles 2 todas as referências em seu código desses locais vão ser mapeados para campos da classe gerado compilador que irá realizar o seu método anônimo. No prisma, estes locais ficar moradores comuns, mas os campos deste campos ocultos são definidos quando você instanciar o método anônimo.

Uma maneira de obter um lambda recursiva, seria a utilização de um tipo de referência para manter o lambda para você.

Tudo isso soa muito mais complicado, então ele realmente é.
2 métodos de realizar seu objetivo:
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) Quando você não quer ter uma referência a esse wrapper, você pode fazê-lo assim:


    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, a razão por trás Prism não seguir C # aqui, é que para threading e loop, esta reutilização de capturado vars faz para problemas de tempo de execução estranhas duras. Em Prism, capturas são realmente capturou o momento de atribuir o método anônimo ou lambda. Que tem um certo toque immuatble a ele ...

Cheers, Robert

O mesmo se aplica a métodos anônimos? Eu estou supondo que ele faz, mas não consigo descobrir a sintaxe para obter este para executar

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

Editar

Ele faz.

  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;

Eu também tentou atribuir a variável:

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

Ainda sem sorte.

Por curiosidade eu tentei algo semelhante usando métodos anônimos.

Basicamente eu implementou uma busca em profundidade em um grafo acíclico direto usando um método anônimo recursiva:

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

Este compilado, mas eu tenho o mesmo erro. NullReferenceException.

Eu também tentei re-implementar o Fibonacci como um método anônimo recursiva:

  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)

novamente mesmo problema! Sempre na segunda iteração (ou seja primeira chamada recursiva)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top