Y- 콤비 네이터 란 무엇입니까? [닫은]


392

Y- 콤비 네이터는 "기능적"측면에서 컴퓨터 과학 개념입니다. 대부분의 프로그래머는 콤비 네이터에 대해 들어 본 적이 없다면 전혀 알지 못합니다.

  • Y- 콤비 네이터 란 무엇입니까?
  • 결합기는 어떻게 작동합니까?
  • 그들은 무엇을 위해 좋은가?
  • 그것들은 절차 적 언어로 유용합니까?

12
약간의 팁, 만약 당신이 나처럼 기능적 언어에 대해 배우고 있다면, 콤비
네이터

3
이 질문 :)의 편집자의 Gravatar에 미소를 얻었다 MADS Torgensen의 블로그에 관련 링크
Benjol


1
나는 Y 컴비 네이터 에 대한 나의 이해를 공유하는 짧은 요점을 썼습니다 : gist.github.com/houtianze/b274e4b975a28fe08aee681699c3f7d0 "Y 컴비 네이터가 재귀 기능을 만드는 방법"을 설명했습니다.
ibic

1
이 질문은 어떻게 "너무 광범위"합니까?
Rey Miyasaka

답변:


201

오랫동안 읽을 준비가 되었다면 Mike Vanier가 훌륭한 설명을 합니다. 간단히 말해 기본적으로 지원하지 않는 언어로 재귀를 구현할 수 있습니다.


14
그래도 링크 이상입니다. 매우 간단한 요약 링크입니다 . 더 긴 요약을 부탁드립니다.
Martijn Pieters

2
그것은 링크 일뿐이지만 이것보다 나아질 수는 없습니다. 이 답변은 종료 할 기본 조건이없는 (add1 투표) 자격이 있습니다. 일명 무한 재귀.
Yavar

7
@Andre MacFie : 노력에 대해서는 언급하지 않았고 품질에 대해서는 언급했습니다. 일반적으로 스택 오버플로에 대한 정책은 추가 정보에 대한 링크와 함께 답변이 자체 포함되어야한다는 것입니다.
Jørgen Fogh

1
@galdre가 맞습니다. 훌륭한 링크이지만 링크 일뿐입니다. 또한 아래의 3 가지 다른 답변에서도 언급되었지만 그 자체만으로도 모든 설명이 잘 뒷받침되어 있습니다. 이 답변은 OP의 질문에 대답조차하지 않습니다.
toraritte

290

Y- 콤비 네이터는 "기능"(다른 기능에서 작동하는 기능)으로, 그 자체에서 함수를 참조 할 수 없을 때 재귀를 가능하게합니다. 컴퓨터 과학 이론에서는 재귀를 일반화 하고 구현을 추상화함으로써 문제의 기능의 실제 작업과 분리합니다. 재귀 함수에 컴파일 타임 이름이 필요하지 않은 이점은 일종의 보너스입니다. =)

람다 함수 를 지원하는 언어로 적용 할 수 있습니다 . 표현람다 기반 특성은 일반적으로 이름으로 자신을 참조 할 수 없음을 의미합니다. 그리고 변수를 선언하고 참조한 다음 람다를 할당하여 자체 참조 루프를 완성하는 방식 으로이 문제를 해결하는 것은 쉽지 않습니다. 람다 변수를 복사하고 원래 변수를 다시 할당하면 자체 참조가 중단됩니다.

Y- 콤비 네이터는 정적 유형 언어 ( 프로 시 저럴 언어가 자주 사용됨)에서 구현하고 사용하기에 번거 롭습니다. 일반적으로 입력 제한 사항은 문제가되는 함수가 컴파일시 알려지기 위해 인수의 수가 필요하기 때문입니다. 즉, y- 콤비 네이터를 사용해야하는 인수 개수에 대해 작성해야합니다.

아래는 C #에서 Y-Combinator의 사용법과 작동 방식의 예입니다.

Y- 콤비 네이터를 사용하는 것은 재귀 함수를 구성하는 "비정상적인"방법을 포함합니다. 먼저 함수 자체보다는 기존 함수를 호출하는 코드 조각으로 함수를 작성해야합니다.

// Factorial, if func does the same thing as this bit of code...
x == 0 ? 1: x * func(x - 1);

그런 다음 함수를 호출하는 함수로 바꾸고 그렇게하는 함수를 반환합니다. 하나의 기능을 사용하고 다른 기능을 수행하는 작업을 수행하기 때문에 기능이라고합니다.

// A function that creates a factorial, but only if you pass in
// a function that does what the inner function is doing.
Func<Func<Double, Double>, Func<Double, Double>> fact =
  (recurs) =>
    (x) =>
      x == 0 ? 1 : x * recurs(x - 1);

이제 함수를 가져 와서 계승처럼 보이는 다른 함수를 반환하지만 자체 호출하는 대신 외부 함수에 전달 된 인수를 호출하는 함수가 있습니다. 이것을 계승으로 만드는 방법은 무엇입니까? 내부 함수를 자체로 전달하십시오. Y-Combinator는 영구 이름을 가진 함수이므로 재귀를 유발할 수 있습니다.

// One-argument Y-Combinator.
public static Func<T, TResult> Y<T, TResult>(Func<Func<T, TResult>, Func<T, TResult>> F)
{
  return
    t =>  // A function that...
      F(  // Calls the factorial creator, passing in...
        Y(F)  // The result of this same Y-combinator function call...
              // (Here is where the recursion is introduced.)
        )
      (t); // And passes the argument into the work function.
}

계승 호출 자체보다는 계승이 계승 생성기 (Y-Combinator에 대한 재귀 호출에 의해 리턴 됨)를 호출합니다. 그리고 t의 현재 값에 따라 생성기에서 반환 된 함수는 t-1로 생성기를 다시 호출하거나 1을 반환하여 재귀를 종료합니다.

복잡하고 비밀 스럽지만 모두 런타임에 흔들리며 작동의 핵심은 "지연된 실행"과 두 기능에 걸친 재귀의 분리입니다. 내부 F는 인수로 전달되어 필요한 경우에만 다음 반복에서 호출됩니다 .


5
왜 오, 왜 그것을 'Y'와 'F'파라미터로 불러야 했습니까! 그들은 형식 ​​인수에서 길을 잃습니다!
Brian Henk

3
Haskell에서는 다음과 같이 추상화 재귀를 수행 할 수 fix :: (a -> a) -> a있으며, acan은 원하는만큼 많은 인수의 함수일 수 있습니다. 이것은 정적 타이핑이 실제로 성가신 것을 의미하지는 않습니다.
Peaker

12
Mike Vanier의 설명에 따르면 Y에 대한 정의는 재귀 적이므로 실제로 결합기가 아닙니다 . "(거의) 명시 적 재귀 제거 (게으른 버전)"에서 C # 코드에 해당하는 게으른 체계를 갖지만 포인트 2에 설명되어 있습니다. "정의 본문의 Y는 자유 변수이기 때문에 결합기가 아닙니다. Y- 콤비 네이터의 멋진 점은 함수의 고정 소수점을 평가하여 재귀를 생성한다는 것입니다. 이런 식으로 명시적인 재귀가 필요하지 않습니다.
GrantJ

@GrantJ 당신은 좋은 지적을합니다. 이 답변을 게시한지 몇 년이 지났습니다. Vanier의 게시물을 훑어 보면 이제 Y를 썼지 만 Y 콤비 네이터는 쓰지 않았습니다. 곧 그의 게시물을 읽고 수정 내용을 게시 할 수 있는지 확인하겠습니다. 내 직감은 C #의 엄격한 정적 타이핑으로 인해 결국 C #을 막을 수 있다고 경고하지만, 내가 할 수있는 것을 볼 수 있습니다.
Chris Ammerman

1
@WayneBurkett 수학에서 꽤 일반적인 관행입니다.
YoTengoUnLCD

102

나는 이것을 몇 년 전에 쓴 설명 인 http://www.mail-archive.com/boston-pm@mail.pm.org/msg02716.html 에서 해제했습니다 .

이 예제에서는 JavaScript를 사용하지만 다른 많은 언어도 작동합니다.

우리의 목표는 변수 1 개만 있고 대입은없고, 이름으로 사물을 정의하는 등의 방법으로 1 개 변수의 재귀 함수를 작성하는 것입니다. (이것이 우리의 목표 인 이유는 다른 질문입니다. 불가능한 것 같아요? 예를 들어 계승을 구현해 봅시다.

1 단계는 우리가 조금만 부정하면 쉽게 할 수 있다고 말하는 것입니다. 2 개의 변수와 대입 함수를 사용하면 최소한 재귀를 설정하기 위해 대입을 사용하지 않아도됩니다.

// Here's the function that we want to recurse.
X = function (recurse, n) {
  if (0 == n)
    return 1;
  else
    return n * recurse(recurse, n - 1);
};

// This will get X to recurse.
Y = function (builder, n) {
  return builder(builder, n);
};

// Here it is in action.
Y(
  X,
  5
);

이제 우리가 덜 속일 수 있는지 봅시다. 우선 과제를 사용하고 있지만 꼭 그럴 필요는 없습니다. X와 Y를 인라인으로 작성할 수 있습니다.

// No assignment this time.
function (builder, n) {
  return builder(builder, n);
}(
  function (recurse, n) {
    if (0 == n)
      return 1;
    else
      return n * recurse(recurse, n - 1);
  },
  5
);

그러나 우리는 1 변수의 함수를 얻기 위해 2 변수의 함수를 사용하고 있습니다. 고칠 수 있을까요? Haskell Curry라는 이름의 똑똑한 사람은 깔끔한 트릭을 가지고 있습니다. 좋은 고차 함수가 있다면 1 변수의 함수 만 필요합니다. 증거는 다음과 같이 완전히 기계적인 텍스트 변환을 통해 2 (또는 일반적인 경우) 이상의 변수에서 1 개의 변수로 얻을 수 있다는 것입니다.

// Original
F = function (i, j) {
  ...
};
F(i,j);

// Transformed
F = function (i) { return function (j) {
  ...
}};
F(i)(j);

어디에서 ... 그대로 유지됩니다. (이 속임수는 발명가의 이름을 따서 "카레 링"이라고합니다. 언어 Haskell은 Haskell Curry의 이름을 따서 명명되었습니다. 쓸모없는 퀴즈 아래있는 파일입니다.) 이제이 변환을 모든 곳에 적용하면 최종 버전을 얻게됩니다.

// The dreaded Y-combinator in action!
function (builder) { return function (n) {
  return builder(builder)(n);
}}(
  function (recurse) { return function (n) {
    if (0 == n)
      return 1;
    else
      return n * recurse(recurse)(n - 1);
  }})(
  5
);

자유롭게 사용해보십시오. alert ()을 반환하면 버튼에 연결하십시오. 이 코드는 할당, 선언 또는 두 변수의 함수를 사용하지 않고 계승을 재귀 적으로 계산합니다. (그러나 작동 방식을 추적하려고 시도하면 헤드 스핀이 발생할 수 있습니다. 파생하지 않고 약간만 다시 포맷하면 코드가 혼란스럽고 혼동 될 수 있습니다.)

재귀 적으로 계승을 정의하는 4 개의 행을 원하는 다른 재귀 함수로 바꿀 수 있습니다.


좋은 설명입니다. 왜 function (n) { return builder(builder)(n);}대신에 builder(builder)쓰셨습니까?
v7d8dpo4

@ v7d8dpo4 카레를 사용하여 2 변수의 함수를 1 변수의 고차 함수로 바꾸고 있었기 때문입니다.
btilly

이것이 폐쇄가 필요한 이유입니까?
TheChetan

1
@TheChetan 클로저를 사용하면 익명 함수 호출 뒤에 사용자 정의 동작을 연결할 수 있습니다. 그것은 또 다른 추상화 기술입니다.
btilly

85

처음부터 이것을 구축하려고 시도 할 때 사용되는지 궁금합니다. 보자 기본적인 재귀 요인 함수는 다음과 같습니다.

function factorial(n) {
    return n == 0 ? 1 : n * factorial(n - 1);
}

fact계산 자체를 수행하는 대신 익명의 계승 계산 함수를 반환하는 새로운 함수를 리팩토링하고 작성해 보겠습니다 .

function fact() {
    return function(n) {
        return n == 0 ? 1 : n * fact()(n - 1);
    };
}

var factorial = fact();

조금 이상하지만 아무 문제가 없습니다. 우리는 각 단계에서 새로운 계승 함수를 생성하고 있습니다.

이 단계에서의 재귀는 여전히 상당히 명시 적입니다. fact함수는 자신의 이름을 인식 할 필요가있다. 재귀 호출을 매개 변수화하십시오.

function fact(recurse) {
    return function(n) {
        return n == 0 ? 1 : n * recurse(n - 1);
    };
}

function recurser(x) {
    return fact(recurser)(x);
}

var factorial = fact(recurser);

훌륭하지만 recurser여전히 자신의 이름을 알아야합니다. 그것도 매개 변수화하자 :

function recurser(f) {
    return fact(function(x) {
        return f(f)(x);
    });
}

var factorial = recurser(recurser);

이제 recurser(recurser)직접 호출하는 대신 결과를 반환하는 래퍼 함수를 ​​만들어 보겠습니다.

function Y() {
    return (function(f) {
        return f(f);
    })(recurser);
}

var factorial = Y();

이제 recurser이름을 완전히 제거 할 수 있습니다 . Y의 내부 함수에 대한 인수 일 뿐이며 함수 자체로 대체 될 수 있습니다.

function Y() {
    return (function(f) {
        return f(f);
    })(function(f) {
        return fact(function(x) {
            return f(f)(x);
        });
    });
}

var factorial = Y();

여전히 참조되는 유일한 외부 이름은 fact이지만, 이제는 쉽게 매개 변수화되어 완전한 일반 솔루션을 생성한다는 것이 분명해졌습니다.

function Y(le) {
    return (function(f) {
        return f(f);
    })(function(f) {
        return le(function(x) {
            return f(f)(x);
        });
    });
}

var factorial = Y(function(recurse) {
    return function(n) {
        return n == 0 ? 1 : n * recurse(n - 1);
    };
});

JavaScript에서 비슷한 설명 : igstan.ro/posts/…
Pops

1
당신이 기능을 소개했을 때 당신은 나를 잃었다 recurser. 그것이 무엇을하고 있는지, 왜 그런지 모릅니다.
Mörre

2
명시 적으로 재귀 적이 지 않은 함수에 대한 일반적인 재귀 솔루션을 구축하려고합니다. recurser은 우리의 재귀 버전을 제공하기 때문에 기능은이 목표를 향한 첫 번째 단계는 fact그 이름 자체를 참조 적이 있습니다.
Wayne

@WayneBurkett 수, 나는이 같은 Y 연결자를 다시 작성 : function Y(recurse) { return recurse(recurse); } let factorial = Y(creator => value => { return value == 0 ? 1 : value * creator(creator)(value - 1); });. 그리고 이것이 내가 그것을 소화하는 방법입니다 (올 바르면 확실하지 않습니다) : 함수를 명시 적으로 참조하지 않으면 ( 콤비 네이터로 허용되지 않음 ) 부분적으로 적용 / 커리 닝 된 함수 (작성자 함수 및 계산 함수)를 사용할 수 있습니다 계산 함수의 이름없이 재귀를 달성하는 람다 / 익명 함수를 만들 수있는 것은 무엇입니까?
neevek

50

위의 답변의 대부분은 Y-콤비가 무엇인지 설명 이다 하지만 무엇인가 에 대해 .

고정 소수점 조합기람다 미적분튜링 완료 되었음을 보여주기 위해 사용됩니다 . 이것은 계산 이론에서 매우 중요한 결과이며 함수형 프로그래밍에 대한 이론적 기초를 제공합니다 .

고정 소수점 조합기를 연구하면 함수형 프로그래밍을 실제로 이해하는 데 도움이되었습니다. 그래도 실제 프로그래밍에서 그 용도를 찾지 못했습니다.


24

JavaScript의 y- 콤비 네이터 :

var Y = function(f) {
  return (function(g) {
    return g(g);
  })(function(h) {
    return function() {
      return f(h(h)).apply(null, arguments);
    };
  });
};

var factorial = Y(function(recurse) {
  return function(x) {
    return x == 0 ? 1 : x * recurse(x-1);
  };
});

factorial(5)  // -> 120

편집 : 코드를 살펴보면 많은 것을 배울 수 있지만 약간의 배경없이 삼키기가 약간 어렵습니다. 죄송합니다. 다른 답변으로 제시된 몇 가지 일반적인 지식을 통해 발생하는 문제를 구분할 수 있습니다.

Y 기능은 "y-combinator"입니다. 이제 var factorialY가 사용되는 줄을 살펴보십시오 . recurse내부 함수에서도 나중에 사용되는 매개 변수 (이 예에서는 )가 있는 함수를 전달 합니다. 매개 변수 이름은 기본적으로 내부 함수의 이름이되어 재귀 호출을 수행 할 수 있습니다 ( recurse()정의에 사용 하기 때문에). 와이.

Y가 마법을 어떻게 사용하는지에 대한 전체 설명을 보려면 링크 된 기사를 확인 하십시오 .


6
당신이 arguments.callee를 사용하여 현재 기능을 액세스 할 수 있기 때문에 자바 스크립트가 익명의 재귀를 할 수있는 Y-콤비를 필요로하지 않는다 (참조 en.wikipedia.org/wiki/... )
xitrium

6
arguments.callee엄격 모드에서는 사용할 수 없습니다. developer.mozilla.org/en/JavaScript/…
dave1010

2
어떤 함수에도 이름을 부여 할 수 있으며, 함수 표현식 인 경우 해당 이름은 함수 자체에만 알려집니다. (function fact(n){ return n <= 1? 1 : n * fact(n-1); })(5)
Esailija

1
IE를 제외하고. kangax.github.io/nfe
VoronoiPotato

18

기능적 프로그래밍에 대해 깊이 경험하지 않고 지금 시작하지 않아도 약간 호기심이 많은 프로그래머에게 :

Y 결합기는 함수가 이름을 가질 수 없지만 인수로 전달되고 반환 값으로 사용되며 다른 함수 내에 정의 될 수있는 상황에서 재귀를 구현할 수있는 수식입니다.

함수 자체를 인수로 전달하여 작동하므로 스스로 호출 할 수 있습니다.

그것은 실제로 수학이지만 효과적으로 프로그래밍 언어이며 컴퓨터 과학, 특히 기능적 프로그래밍의 기초가되는 람다 미적분학의 일부입니다.

프로그래밍 언어가 함수의 이름을 지정하는 경향이 있기 때문에 Y 결합기의 일상적인 실제 가치는 제한되어 있습니다.

경찰 라인업에서 식별해야 할 경우 다음과 같습니다.

Y = λf. (λx.f (xx)) (λx.f (xx))

당신은 일반적으로 반복 때문에 그것을 발견 할 수 있습니다 (λx.f (x x)).

λ기호는 그 이름 수학 람다를 제공 그리스 문자 람다이다, 그리고 많은 거기에 (λx.t)그 어떤 람다 계산법의 모습처럼 때문에 스타일 용어.


이것이 정답이어야합니다. BTW,와 U x = x x, Y = U . (. U)(표기 하스켈-처럼 남용). 적절한 조합기를 갖춘 IOW Y = BU(CBU). 따라서 Yf = U (f . U) = (f . U) (f . U) = f (U (f . U)) = f ((f . U) (f . U)).
Will Ness

13

익명 재귀

고정 소수점 조합기는 fix정의에 의해 동등성을 만족시키는 고차 함수 입니다.

forall f.  fix f  =  f (fix f)

fix fx고정 소수점 방정식에 대한 해 를 나타냅니다

               x  =  f x

자연수의 계승은 다음과 같이 증명할 수 있습니다.

fact 0 = 1
fact n = n * fact (n - 1)

을 사용하면 fix일반 / μ- 재귀 함수에 대한 임의의 구성 증명이 비영리 자기 참조없이 파생 될 수 있습니다.

fact n = (fix fact') n

어디

fact' rec n = if n == 0
                then 1
                else n * rec (n - 1)

그런

   fact 3
=  (fix fact') 3
=  fact' (fix fact') 3
=  if 3 == 0 then 1 else 3 * (fix fact') (3 - 1)
=  3 * (fix fact') 2
=  3 * fact' (fix fact') 2
=  3 * if 2 == 0 then 1 else 2 * (fix fact') (2 - 1)
=  3 * 2 * (fix fact') 1
=  3 * 2 * fact' (fix fact') 1
=  3 * 2 * if 1 == 0 then 1 else 1 * (fix fact') (1 - 1)
=  3 * 2 * 1 * (fix fact') 0
=  3 * 2 * 1 * fact' (fix fact') 0
=  3 * 2 * 1 * if 0 == 0 then 1 else 0 * (fix fact') (0 - 1)
=  3 * 2 * 1 * 1
=  6

이 공식적인 증거는

fact 3  =  6

재 작성을 위해 고정 소수점 조합기를 동등하게 사용

fix fact'  ->  fact' (fix fact')

람다 미적분학

이 지정되지 않은 람다 미적분 형식주의는 문맥 자유 문법에있다

E ::= v        Variable
   |  λ v. E   Abstraction
   |  E E      Application

여기서 v함께와 변수 범위에 걸쳐 베타ETA 환원 규칙

(λ x. B) E  ->  B[x := E]                                 Beta
  λ x. E x  ->  E          if x doesn’t occur free in E   Eta

베타 축소 는 표현 ( "인수")으로 x추상화 ( "함수") 본문에서 변수의 모든 자유 발생을 대체합니다 . 에타 감소는 중복 추상화를 제거합니다. 때로는 형식주의에서 생략되기도합니다. 기약 더 감소 규칙이 적용되지되는 식은,에 정상 또는 정규형 .BE

λ x y. E

속기

λ x. λ y. E

(추상 다원성),

E F G

속기

(E F) G

(응용 프로그램 왼쪽 연관성),

λ x. x

λ y. y

아르 알파 등가 .

추상화와 응용은 람다 미적분학의 유일한 "언어 프리미티브"이지만, 임의로 복잡한 데이터와 연산을 인코딩 할 수 있습니다.

교회 숫자는 Peano-axiomatic naturals와 유사한 자연수를 인코딩 한 것입니다.

   0  =  λ f x. x                 No application
   1  =  λ f x. f x               One application
   2  =  λ f x. f (f x)           Twofold
   3  =  λ f x. f (f (f x))       Threefold
    . . .

SUCC  =  λ n f x. f (n f x)       Successor
 ADD  =  λ n m f x. n f (m f x)   Addition
MULT  =  λ n m f x. n (m f) x     Multiplication
    . . .

공식적인 증거

1 + 2  =  3

베타 축소의 다시 쓰기 규칙 사용 :

   ADD                      1            2
=  (λ n m f x. n f (m f x)) (λ g y. g y) (λ h z. h (h z))
=  (λ m f x. (λ g y. g y) f (m f x)) (λ h z. h (h z))
=  (λ m f x. (λ y. f y) (m f x)) (λ h z. h (h z))
=  (λ m f x. f (m f x)) (λ h z. h (h z))
=  λ f x. f ((λ h z. h (h z)) f x)
=  λ f x. f ((λ z. f (f z)) x)
=  λ f x. f (f (f x))                                       Normal form
=  3

조합기

람다 미적분학에서 결합기 는 자유 변수가없는 추상화입니다. 가장 간단하게 : IID 조합 자

λ x. x

항등 함수에 동형

id x = x

이러한 결합기 는 SKI 시스템과 같은 결합기 계산 의 기본 연산자입니다 .

S  =  λ x y z. x z (y z)
K  =  λ x y. x
I  =  λ x. x

베타 감소는 강력하게 정상화 되지 않습니다 . 모든 환원성 표현 인 "환원"이 베타 감소 하에서 정상적인 형태로 수렴되는 것은 아닙니다. 간단한 예는 오메가 ω콤비 네이터의 다양한 적용입니다

λ x. x x

그 자체로 :

   (λ x. x x) (λ y. y y)
=  (λ y. y y) (λ y. y y)
. . .
=  _|_                     Bottom

가장 왼쪽 하위 표현 ( "헤드")의 감소가 우선합니다. 적용 순서는 대체 전에 인수를 정규화하지만 정상 순서 는 그렇지 않습니다. 두 가지 전략은 열성적인 평가 (예 : C)와 게으른 평가 (예 : Haskell)와 유사합니다.

   K          (I a)        (ω ω)
=  (λ k l. k) ((λ i. i) a) ((λ x. x x) (λ y. y y))

열렬한 적용 순서 베타 감소로 분기

=  (λ k l. k) a ((λ x. x x) (λ y. y y))
=  (λ l. a) ((λ x. x x) (λ y. y y))
=  (λ l. a) ((λ y. y y) (λ y. y y))
. . .
=  _|_

엄격한 의미론 이후

forall f.  f _|_  =  _|_

그러나 게으른 정상 순서 베타 감소로 수렴

=  (λ l. ((λ i. i) a)) ((λ x. x x) (λ y. y y))
=  (λ l. a) ((λ x. x x) (λ y. y y))
=  a

표현식의 형식이 정상인 경우 정규 차수 베타 감소로이를 찾습니다.

와이

고정 소수점 조합기 의 필수 특성Y

λ f. (λ x. f (x x)) (λ x. f (x x))

~에 의해 주어진다

   Y g
=  (λ f. (λ x. f (x x)) (λ x. f (x x))) g
=  (λ x. g (x x)) (λ x. g (x x))           =  Y g
=  g ((λ x. g (x x)) (λ x. g (x x)))       =  g (Y g)
=  g (g ((λ x. g (x x)) (λ x. g (x x))))   =  g (g (Y g))
. . .                                      . . .

동등성

Y g  =  g (Y g)

동형이다

fix f  =  f (fix f)

형식화되지 않은 람다 미적분은 일반 / μ 재귀 함수에 대한 임의의 구성 적 증거를 인코딩 할 수 있습니다.

 FACT  =  λ n. Y FACT' n
FACT'  =  λ rec n. if n == 0 then 1 else n * rec (n - 1)

   FACT 3
=  (λ n. Y FACT' n) 3
=  Y FACT' 3
=  FACT' (Y FACT') 3
=  if 3 == 0 then 1 else 3 * (Y FACT') (3 - 1)
=  3 * (Y FACT') (3 - 1)
=  3 * FACT' (Y FACT') 2
=  3 * if 2 == 0 then 1 else 2 * (Y FACT') (2 - 1)
=  3 * 2 * (Y FACT') 1
=  3 * 2 * FACT' (Y FACT') 1
=  3 * 2 * if 1 == 0 then 1 else 1 * (Y FACT') (1 - 1)
=  3 * 2 * 1 * (Y FACT') 0
=  3 * 2 * 1 * FACT' (Y FACT') 0
=  3 * 2 * 1 * if 0 == 0 then 1 else 0 * (Y FACT') (0 - 1)
=  3 * 2 * 1 * 1
=  6

(곱셈 지연, 합류)

Churchian 형식화되지 않은 람다 미적분학의 경우, 재귀 적으로 열거 가능한 고정 소수점 조합기의 무한대가 존재하는 것으로 나타났습니다 Y.

 X  =  λ f. (λ x. x x) (λ x. f (x x))
Y'  =  (λ x y. x y x) (λ y x. y (x y x))
 Z  =  λ f. (λ x. f (λ v. x x v)) (λ x. f (λ v. x x v))
 Θ  =  (λ x y. y (x x y)) (λ x y. y (x x y))
  . . .

정규 차수 베타 감소는 확장되지 않은 유형화되지 않은 람다 미적분을 Turing-complete rewrite 시스템으로 만듭니다.

Haskell에서는 고정 소수점 조합기를 우아하게 구현할 수 있습니다.

fix :: forall t. (t -> t) -> t
fix f = f (fix f)

하스켈의 게으름은 모든 하위 표현이 평가되기 전에 무한대로 정규화됩니다.

primes :: Integral t => [t]
primes = sieve [2 ..]
   where
      sieve = fix (\ rec (p : ns) ->
                     p : rec [n | n <- ns
                                , n `rem` p /= 0])


4
대답의 철저함에 감사하지만 첫 줄 바꿈 후 공식적인 수학 배경이 거의없는 프로그래머에게는 결코 접근 할 수 없습니다.
Jared Smith

4
@ jared-smith이 대답은 Y 조합기의 CS / 수학 개념에 대한 보충적인 Wonkaian 이야기를 제공하기위한 것입니다. 아마도 친숙한 개념에 대한 가능한 가장 좋은 유추는 이미 다른 답변자에 의해 그려 졌다고 생각합니다. 개인적으로, 나는 항상 좋은 비유를 통해 아이디어 의 근본적인 진기함 인 진정한 기원에 직면하는 것을 좋아했습니다 . 나는 가장 광범위한 유추를 부적절하고 혼란스럽게 생각합니다.

1
안녕하세요, 신원 조합기 λ x . x, 오늘 어떠세요?
MaiaVictor

이 답변이 가장 마음에 듭니다 . 방금 모든 질문을 해결했습니다!
학생

11

다른 답변은 하나의 중요한 사실없이 이것에 대해 매우 간결한 답변을 제공합니다.이 복잡한 방식으로 실제 언어로 고정 소수점 조합기를 구현할 필요가 없으며 실제적인 목적을 제공하지 않습니다 ( "예외, 나는 Y 조합을 알고 있습니다. ")입니다. 중요한 이론적 개념이지만 실질적인 가치는 거의 없습니다.


6

다음은 Y-Combinator 및 Factorial 함수의 JavaScript 구현입니다 (Douglas Crockford의 기사 : http://javascript.crockford.com/little.html 참조 ).

function Y(le) {
    return (function (f) {
        return f(f);
    }(function (f) {
        return le(function (x) {
            return f(f)(x);
        });
    }));
}

var factorial = Y(function (fac) {
    return function (n) {
        return n <= 2 ? n : n * fac(n - 1);
    };
});

var number120 = factorial(5);

6

Y- 콤비 네이터는 플럭스 커패시터의 다른 이름입니다.


4
매우 재밌습니다. :) 젊은 사람들은 참고 문헌을 인식하지 못할 수도 있습니다.
Will Ness

2
ㅋ! 그래, 젊은 사람은 (나)는 여전히 ... 이해할 수

나는 그것이 진짜라고 생각했고 나는 여기서 끝났다. youtube.com/watch?v=HyWqxkaQpPw 최근 기사 futurism.com/scientists-made-real-life-flux-capacitor
Thinkar Nay Htoo

이 답변은 영어가 아닌 사람들에게 특히 혼란 스러울 수 있다고 생각합니다. 이 주장을 이해하기 위해 꽤 많은 시간을 할애 할 수도 있습니다. (이것이 마음에 든다면이 질문에 답하고 배우는 사람이 낙담 한 것을 알게되면 기분이 나빠질 것입니다)
mike

5

저는 Clojure와 Scheme의 Y-Combinator에 일종의 "바보 가이드"를 작성하여 스스로를 이해하는 데 도움을주었습니다. "The Little Schemer"의 자료에 영향을받습니다.

계획에서 : https://gist.github.com/z5h/238891

또는 Clojure : https://gist.github.com/z5h/5102747

두 튜토리얼 모두 주석과 함께 코드가 삽입되어 있으며 좋아하는 편집기로 잘라서 붙여 넣을 수 있습니다.


5

콤비 네이터 초보자 인 Mike Vanier의 기사를 찾았습니다. (Nicholas Mancuso 덕분에)가 정말 도움이된다는 . 이해를 문서화하는 것 외에도 다른 사람들에게 도움이 될 수 있다면 요약을 작성하고 싶습니다.

에서 엉터리적은 엉터리

계승을 예로 사용하여 다음 almost-factorial함수를 사용하여 숫자의 계승을 계산합니다.x .

def almost-factorial f x = if iszero x
                           then 1
                           else * x (f (- x 1))

위의 의사 코드 almost-factorial에서 기능 f과 숫자를 받습니다.x ( almost-factorial커리되므로 함수를 수행 f하고 1-arity 함수를 반환하는 것으로 볼 수 있습니다 ).

almost-factorial계승을 계산할 때 x의 계승 계산을 위임합니다.x - 1f 결과 를 다음 과 같이 누적합니다.x (- X 1)이 경우의 결과 (X 곱한다).

그것은 팩토리얼 함수 almost-factorialcrappy 버전 (tilt 만 계산할 수 있음 x - 1) 을 취하고 덜 클래시 버전의 factorial (tilt을 계산 )을 반환하는 것으로 볼 수 있습니다 x. 이 양식에서와 같이 :

almost-factorial crappy-f = less-crappy-f

크롤링적은 버전의 factorial을 반복해서 전달하면 almost-factorial원하는 factorial 함수를 얻게됩니다 f. 다음과 같이 간주 될 수있는 곳 :

almost-factorial f = f

수정 점

almost-factorial f = f의미 f는 기능 의 수정 점 입니다almost-factorial .

이것은 위의 함수의 관계를 보는 정말 흥미로운 방법이었습니다. (그렇지 않은 경우 수정 지점에 대한 Mike의 게시물을 읽으십시오)

세 가지 기능

일반화하기 위해, 우리는이 비 재귀 기능 fn(우리의 거의 계승 등) 우리는이 수정 포인트 기능을 fr(우리 F 등), 그 다음 무엇을 Y수행하면 줄 때입니다 Y fn, Y의 고정 소수점 기능을 반환합니다 fn.

따라서 요약하면 ( fr단 하나의 매개 변수 만 사용 한다고 가정하면 단순화됩니다 . 재귀에서 , ...로 x퇴화합니다 ).x - 1x - 2

  • 우리는 핵심 계산fn다음 def fn fr x = ...accumulate x with result from (fr (- x 1))과 같이 정의합니다 . 이것은 거의 유용한 기능입니다. fn직접 사용할 수는 없지만 x곧 유용 할 것입니다. 이 비 재귀 fn는 함수 fr를 사용하여 결과를 계산합니다.
  • fn fr = fr, fr의 수정 점이다 fn, fr는 IS 유용한 funciton, 우리가 사용할 수 있습니다 frx우리의 결과를 얻을 수
  • Y fn = fr, Y함수의 수정 점을 반환 Y 하고 거의 유용한 함수 fn유용하게 만듭니다. fr

파생 Y(포함되지 않음)

나는 파생을 건너 뛰고 Y이해로 간다 Y. Mike Vainer의 게시물에는 많은 세부 정보가 있습니다.

의 형태 Y

Y( 람다 미적분학 형식으로) 다음 과 같이 정의됩니다 .

Y f = λs.(f (s s)) λs.(f (s s))

s함수의 왼쪽에 있는 변수 를 바꾸면

Y f = λs.(f (s s)) λs.(f (s s))
=> f (λs.(f (s s)) λs.(f (s s)))
=> f (Y f)

실제로 결과 (Y f)f .

(Y f) 작동합니까?

의 서명을 따라 f, (Y f)단순화하기 위해, 모든 인수에 대응하는 기능을 할 수있다, 이제 가정하자 (Y f)우리의 계승 기능처럼, 하나 개의 매개 변수를 사용합니다.

def fn fr x = accumulate x (fr (- x 1))

이후 fn fr = fr, 우리는 계속

=> accumulate x (fn fr (- x 1))
=> accumulate x (accumulate (- x 1) (fr (- x 2)))
=> accumulate x (accumulate (- x 1) (accumulate (- x 2) ... (fn fr 1)))

가장 안쪽 (fn fr 1)이 기본 사례이고 fn사용하지 않으면 재귀 계산이 종료됩니다.fr 계산에 .

Y다시 보고 :

fr = Y fn = λs.(fn (s s)) λs.(fn (s s))
=> fn (λs.(fn (s s)) λs.(fn (s s)))

그래서

fr x = Y fn x = fn (λs.(fn (s s)) λs.(fn (s s))) x

나 에게이 설정의 마법 부분은 다음과 같습니다.

  • fn그리고 fr서로 상호 의존하십시오 : fr'포장' fn내부, 매번 fr계산에 사용되며 x, '스폰'( '리프트'?) fn및 계산을 위임합니다 fn(자체 전달 frx). 반면 에 작은 문제의 결과를 계산 하는 데 fn의존 fr하고 사용 fr합니다 x-1.
  • 당시는 fr정의에 사용 되었지만 ( 작업에서 사용 하는 fn경우 ) 실제 는 아직 정의되지 않았습니다.fnfrfr
  • 그것은의 fn실제 비즈니스 로직을 정의한다. 기준 fn, Y생성 fr- 특정 형태의 도우미 함수 -에 대한 계산을 용이하게하기 위해 fnA의 순환 방법.

현재이 Y방법을 이해하는 데 도움이되었으므로 도움 이 되길 바랍니다.

BTW, Lambda Calculus를 통한 함수형 프로그래밍에 대한 소개 매우 훌륭하다는 것을 알았 Y습니다.


5

다음은 Nicholas Mancuso답변에 언급 된 기사 (TOTALY 가치가 있는 기사) 에서 컴파일 한 원래 질문 에 대한 답변 과 다른 답변입니다.

Y- 콤비 네이터 란 무엇입니까?

Y- 콤비 네이터는 재귀 적이 지 않은 함수 인 단일 인수를 취하는 "함수"(또는 고차 함수-다른 함수에서 작동하는 함수)로, 함수의 버전을 반환합니다. 재귀.


다소 재귀 적 =)이지만 더 깊이있는 정의 :

콤비 네이터 — 자유 변수가없는 람다 식입니다.
자유 변수 — 바운드 변수가 아닌 변수입니다.
바운드 변수 — 변수 이름을 인수 중 하나로 갖는 람다 식 본문에 포함 된 변수입니다.

이것을 생각하는 또 다른 방법은 콤비 네이터가 람다 식이며, 콤비 네이터의 이름을 찾은 곳마다 그 정의로 바꿀 수 있으며 모든 것이 여전히 작동합니다 (콤비 네이터가 람다 몸체 내부에 자체 참조 포함).

Y- 콤비 네이터는 고정 소수점 조합기입니다.

함수의 고정 점은 함수에 의해 자체적으로 매핑되는 함수 도메인의 요소입니다.
즉, 이것이 의미하는 경우 c함수의 고정 점입니다.f(x)f(c) = c
f(f(...f(c)...)) = fn(c) = c

결합기는 어떻게 작동합니까?

아래 예제는 강력한 + 동적 타이핑을 가정합니다 .

게으른 (정상 순서) Y- 조합기 :
이 정의는 게으른 (또한 지연되고 필요에 따라) 평가가있는 언어에 적용됩니다. 평가 전략은 값이 필요할 때까지 식의 평가를 지연시킵니다.

Y = λf.(λx.f(x x)) (λx.f(x x)) = λf.(λx.(x x)) (λx.f(x x))

이것이 의미하는 것은 주어진 함수 f(비 재귀 함수)에 대해 먼저 계산하고 해당 λx.f(x x)람다 식을 자체에 적용 하여 해당 재귀 함수를 얻을 수 있다는 것입니다.

엄격한 (적용 순서) Y- 조합기 :
이 정의는 엄격한 (열심하고 욕심 많은) 평가-변수에 바인딩되자 마자식이 평가되는 평가 전략이있는 언어에 적용됩니다.

Y = λf.(λx.f(λy.((x x) y))) (λx.f(λy.((x x) y))) = λf.(λx.(x x)) (λx.f(λy.((x x) y)))

그것은 본질적으로 게으른 것과 동일하며 λ람다의 신체 평가를 지연시키기 위해 여분의 래퍼가 있습니다. 내가 물었다 한 또 다른 질문 약간이 주제와 관련된를.

그들은 무엇을 위해 좋은가?

Chris Ammerman이 대답 에서 빌린 도난 : Y- 콤비 네이터는 재귀를 일반화하고 구현을 추상화하여 해당 함수의 실제 작업과 분리합니다.

비록 Y- 콤비 네이터는 실제적인 응용을 가지고 있지만, 주로 이론적 인 개념으로, 전체적인 비전을 넓히고 분석 능력과 개발자 기술을 향상시킬 것입니다.

그것들은 절차 적 언어로 유용합니까?

으로 마이크 그리고 Vanier에 의해 진술 : 는 Y 콤비 자체가 아무튼 때문에 ', 많은 정적으로 입력 된 언어의 Y 연결자를 정의하는 것이 가능하지만 (내가 본 예에서 적어도) 이러한 정의는 일반적으로 일부 비 명백한 유형의 해커를 필요로 t는 정적 유형이 간단합니다. 그것은이 기사의 범위를 넘어서므로 더 이상 언급하지 않을 것이다.

로 그리고 크리스 Ammerman 언급 대부분의 절차 언어 정전기 입력을 가지고있다.

따라서 이것에 대답하십시오 – 실제로는 아닙니다.


4

y 결합기는 익명의 재귀를 구현합니다. 그래서 대신

function fib( n ){ if( n<=1 ) return n; else return fib(n-1)+fib(n-2) }

넌 할 수있어

function ( fib, n ){ if( n<=1 ) return n; else return fib(n-1)+fib(n-2) }

물론 y- 콤비 네이터는 이름 별 통화 언어에서만 작동합니다. 일반적인 값별 호출 언어로 이것을 사용하려면 관련 z 조합기 (y 조합기가 분기 / 무한 루프)가 필요합니다.


Y 콤비 네이터는 값별 통과 및 지연 평가와 함께 작동 할 수 있습니다.
Quelklef 2016 년

3

고정 소수점 조합기 (또는 고정 소수점 연산자)는 다른 함수의 고정 소수점을 계산하는 고차 함수입니다. 이 작업은 언어 런타임 엔진의 명시적인 지원없이 재 작성 규칙의 형태로 재귀를 구현할 수 있기 때문에 프로그래밍 언어 이론과 관련이 있습니다. (src 위키 백과)


3

이 운영자는 당신의 삶을 단순화시킬 수 있습니다 :

var Y = function(f) {
    return (function(g) {
        return g(g);
    })(function(h) {
        return function() {
            return f.apply(h(h), arguments);
        };
    });
};

그런 다음 추가 기능을 피하십시오.

var fac = Y(function(n) {
    return n == 0 ? 1 : n * this(n - 1);
});

마지막으로을 호출 fac(5)합니다.


0

이것에 대답하는 가장 좋은 방법은 JavaScript와 같은 언어를 선택하는 것입니다.

function factorial(num)
{
    // If the number is less than 0, reject it.
    if (num < 0) {
        return -1;
    }
    // If the number is 0, its factorial is 1.
    else if (num == 0) {
        return 1;
    }
    // Otherwise, call this recursive procedure again.
    else {
        return (num * factorial(num - 1));
    }
}

이제 함수 내부에서 함수 이름을 사용하지 않고 재귀 적으로 호출하도록 다시 작성하십시오.

함수 이름 factorial은 콜 사이트 에서만 볼 수 있습니다.

힌트 : 함수 이름은 사용할 수 없지만 매개 변수 이름은 사용할 수 있습니다.

문제를 해결하십시오. 찾지 마십시오. 일단 해결하면 y 조합기가 해결하는 문제를 이해할 수 있습니다.


1
해결하는 것보다 더 많은 문제가 발생하지 않습니까?
녹 티스 스카이 타워

1
녹 티스, 질문을 명확히 할 수 있습니까? y- 콤비 네이터 자체의 개념이 해결하는 것보다 더 많은 문제를 일으키는 지 묻고 있습니까, 아니면 특히 JavaScript를 사용하여 시연하기로 선택한 것에 대해 구체적으로 이야기하고 있습니까? 내가 설명 했어?
zumalifeguard
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.