2009-08-03 3 views
0

최근에 .net 3.0 (windows forms, C#)으로 옮겼습니다. 나는 술어와 람다 표현에 대해 더 알고 싶다. 우리는 어디에서 사용해야합니까? 성능을 개선합니까? 내부적으로 어떻게 작동할까요? 감사.술어와 람다 표현

답변

5

스택 오버플로를 검색하면 해당 항목을 설명하는 1,000 가지 답변을 찾을 수 있습니다. 즉, 람다는 익명 메소드를 다른 메소드에 전달하고자하는 시점에 익명 메소드를 작성하는 방법입니다. 익명 메소드의 경우 delegate 구문과 기술적으로 동일하지만 형식 유추 기능이 추가되었으므로 매개 변수 유형을 명시 할 필요가 없습니다. 술어는 어떤 값을 받아들이고 bool을 리턴하는 메소드입니다 - 예는 Where의 인수가됩니다.

외부 변수를 참조하지 않는 람다는 구성된 정적 메서드로 변환됩니다. 포함하는 클래스의 인스턴스 멤버를 참조하면 인스턴스 메서드가됩니다. 로컬 변수를 참조하는 경우 해당 변수는 둘러싼 메서드가 실행되기 시작할 때 할당되는 컴파일러에서 생성 된 클래스의 필드로 "끌어 올리며"람다 본문은 새 클래스의 메서드가됩니다.

성능면에서 그다지 큰 차이는 없습니다. 임시 객체 생성과 관련이 있지만 I find that these are collected extremely efficiently by the GC입니다.

1

다른 버전의 C#을 연구하고 어떻게 다른지 궁금하다면. 제 제안은 C.Sharp.in.Depth by jon skeet을 읽으십시오. 이렇게하면 새 버전을 더 잘 이해할 수 있습니다.

0

성능이 향상 되었습니까? 그리고 그들이 내부적으로 어떻게 일하는가는 입니다. 감사.

대부분의 경우 성능이 저하 될 수 있습니다. 그러나 성능을 죽일 수있는 몇 가지 병리학 적 사례, 즉 fixed point combinators의 과도한 사용이 있습니다.

그것의 우리가 재귀 람다 함수를 작성하기 위해 Y-콤비를 사용할 수있는 잘 알려진 트릭 그러나 다음 코드를 고려하십시오

using System; 
using System.Diagnostics; 

namespace YCombinator 
{ 
    class Program 
    { 
     static Func<T, U> y<T, U>(Func<Func<T, U>, Func<T, U>> f) 
     { 
      return f(x => y<T, U>(f)(x)); 
     } 

     static int fibIter(int n) 
     { 
      int fib0 = 0, fib1 = 1; 
      for (int i = 1; i <= n; i++) 
      { 
       int tmp = fib0; 
       fib0 = fib1; 
       fib1 = tmp + fib1; 
      } 
      return fib0; 
     } 

     static Func<int, int> fibCombinator() 
     { 
      return y<int, int>(f => n => 
      { 
       switch (n) 
       { 
        case 0: return 0; 
        case 1: return 1; 
        default: return f(n - 1) + f(n - 2); 
       } 
      }); 
     } 

     static int fibRecursive(int n) 
     { 
      switch (n) 
      { 
       case 0: return 0; 
       case 1: return 1; 
       default: return fibRecursive(n - 1) + fibRecursive(n - 2); 
      } 
     } 

     static void Benchmark(string msg, int iterations, Func<int, int> f) 
     { 
      int[] testCases = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20 }; 
      Stopwatch watch = Stopwatch.StartNew(); 
      for (int i = 0; i <= iterations; i++) 
      { 
       foreach (int n in testCases) 
       { 
        f(n); 
       } 
      } 
      watch.Stop(); 
      Console.WriteLine("{0}: {1}", msg, watch.Elapsed.TotalMilliseconds); 
     } 

     static void Main(string[] args) 
     { 
      int iterations = 10000; 
      Benchmark("fibIter", iterations, fibIter); 
      Benchmark("fibCombinator", iterations, fibCombinator()); 
      Benchmark("fibRecursive", iterations, fibRecursive); 
      Console.ReadKey(true); 
     } 
    } 
} 

이 프로그램은 인쇄 아웃 :

fibIter: 14.8074 
fibCombinator: 61775.1485 
fibRecursive: 2591.2444

fibCombinator 및 fibRecursive는 기능적으로 동일하며 계산 복잡도는 동일하지만 fibCombinator는 모든 중간 개체 할당으로 인해 전체 4100x 느립니다.

+1

빌드되지 않은 언어의 역사에 대해 스스로 가르치고 싶지 않다면 C# 프로그래머 (또는 포스트 Algol 언어의 사용자)는 재귀를 수행하기 위해 Y 결합자를 사용하지 않을 것입니다 - 자신을 참조 할 수있는 명명 된 함수의 개념. –

+0

@Earwicker : 좋은 지적 :) 당신은 C#에서는 이와 같은 코드를 작성하지 않았지만, 결합 자와 point-free 스타일은 모두 F #, OCaml 및 Haskell의 분노입니다. 필자는 F #에서 combinator와 point-free 스타일을 실험했으며, 거의 모든 경우에 combinator 버전이 성능을 저하시킵니다. – Juliet