문제

나는 산소에서 람다 발현을 실험 해왔다. Fibonacci 번호를 계산하기위한 매우 간단한 재귀 람다 발현 :

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

이 코드를 실행하면 NullReferenceException이 나타납니다. 내가 무엇을 잘못하고 있는지에 대한 아이디어가 있습니까?

도움이 되었습니까?

해결책

당신은 아무 문제가 없습니다. 무엇이든, 컴파일러는 람다 본체 내부에 할당되지 않은 변수 인 FIB를 사용하는 것에 대해 경고해야합니다.

그러나 컴파일러는 FIB를 위치로 캡처해야하므로 할당이 완료되고 대의원이 나중에 호출되면 FIB가 올바르게 할당되고 재귀가 예상대로 작동해야합니다.

실패의 가장 명백한 이유는 프리즘이 위치를 캡처하는 것이 아니라 값이 지체적이지 않으며 비 지구 언어의 다른 모든 폐쇄 구현과 상충 될 수 있기 때문입니다.

예를 들어,이 코드를 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를 각각 클릭 한 후에 알림 상자는 각각 1과 2를 보여줍니다. Prism/Oxygene Semantics는 1 번 모두 표시됩니다.

다른 팁

스티브 :

이 문제는 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);

메시지 상자는 값 34를 보여줍니다.

Jeroen의 질문에 대한 응답 으로이 코드는 원래 공식 릴리스 빌드 인 3.0.21.661에서 실행되었습니다.

임시 해결 방법으로 사용할 수 있습니다.

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

프리즘은 네이티브 델파이 또는 C#과는 다른 지역 변수의 캡처를 다르게 처리합니다. 해당 지역 주민의 코드의 모든 참조는 익명 방법을 보유 할 컴파일러 생성 클래스의 필드에 매핑됩니다. 프리즘에서는이 지역 주민들이 평범한 현지인을 유지하지만 익명의 방법을 인스턴스화 할 때이 숨겨진 필드의 들판이 설정됩니다.

재귀 람다를 얻는 한 가지 방법은 람다를 고정하기 위해 참조 유형을 사용하는 것입니다.

이 모든 것이 실제로는 훨씬 더 복잡하게 들립니다.
목표 달성 방법 :
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#을 따르지 않는 이유 인 BTW는 스레딩 및 루프의 경우 캡처 된 VAR을 재사용하면 이상한 런타임 문제가 발생하기 때문입니다. 프리즘에서 캡처는 익명의 방법이나 람다를 할당하는 순간 실제로 포착됩니다. 그것은 그것에 대해 특정한 손길이 있습니다 ...

건배, 로버트

익명 방법에도 동일하게 적용됩니까? 나는 그것을 추측하고 있지만, 이것을 실행하기 위해 구문을 알 수는 없습니다.

  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;

또한 변수를 할당하려고 시도했습니다.

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

여전히 운이 없습니다.

호기심으로 익명의 방법을 사용하여 비슷한 것을 시도했습니다.

기본적으로 나는 재귀적인 익명 방법을 사용하여 직접적인 아시 클릭 그래프에서 깊이의 첫 번째 검색을 구현했습니다.

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

이것은 컴파일되었지만 같은 오류가 발생했습니다. nullReferenceException.

또한 Fibonacci를 재귀적인 익명 방법으로 재 구현하려고 노력했습니다.

  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)

다시 같은 문제! 항상 두 번째 반복에 (즉, 첫 번째 재귀 호출)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top