it-swarm-tr.com

Y-birleştiricisi nedir?

Y-birleştirici, nesnelerin “işlevsel” yönünden bir bilgisayar bilimi kavramıdır. Programcıların çoğu, onlar hakkında bir şey duymuşlarsa, birleştiriciler hakkında pek bir şey bilmezler.

  • Y-birleştiricisi nedir?
  • Birleştiriciler nasıl çalışır?
  • Neye iyi gelirler?
  • Usul dillerinde faydalılar mı?
372
Chris Ammerman

Uzun bir okumaya hazırsanız, Mike Vanier'in bir harika bir açıklaması vardır . Uzun lafın kısası, özyinelemeyi doğal olarak desteklemeyen bir dilde uygulamanıza olanak tanır.

194
Nicholas Mancuso

Bir Y-birleştiricisi, işlevden kendi içinden bahsedemediğiniz zaman, özyinelemeyi sağlayan bir işlevdir (diğer işlevlerde çalışan bir işlevdir). Bilgisayar bilimi teorisinde, , özyinelemeyi uygular, soyutlar ve böylece söz konusu işlevin fiili çalışmasından ayırır. Özyinelemeli işlevi için bir derleme zamanı adı gerekmeyen yararı bir tür bonus. =)

Bu, lambda işlevlerini destekleyen dillerde uygulanabilir. İfadesi temelli lambda doğası genellikle adlarına göre kendilerine başvuramadıkları anlamına gelir. Ve bunun etrafında, değişkeni bildirerek, ona referans vererek, sonra lambdayı kendisine atayarak, kendi kendine referans döngüsünü tamamlamak için çalışmak, kırılgandır. Lambda değişkeni kopyalanabilir ve orijinal değişken kendi kendine referansı kesen yeniden atanır.

Y-birleştiricileri, statik yazılan dilleri (genellikle prosedürel dilleri sıkça kullanmaktadırlar) dillerinde (ve (===) prosedürel dilleri (genellikle)) kullanmaları zordur. Derleme zamanında bilinmesi için söz konusu işlevin argüman sayısını gerektirir. Bu, kullanılması gereken herhangi bir argüman sayısı için bir y-birleştiricisinin yazılması gerektiği anlamına gelir.

Aşağıda, bir Y-Birleştiricisinin C # 'da nasıl kullanıldığına ve nasıl çalıştığına bir örnek verilmiştir.

Y-birleştiricinin kullanılması, özyinelemeli bir işlev oluşturmanın "sıradışı" bir yolunu içerir. İlk önce, işlevinizi kendisinden ziyade önceden var olan bir işlevi çağıran bir kod parçası olarak yazmalısınız:

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

Sonra onu çağırmak için bir işlevi alan ve bunu yapan bir işlevi döndüren bir işleve dönüştürürsünüz. Buna işlevsel denir, çünkü bir işlevi alır ve başka bir işlevle sonuçlanan bir işlem gerçekleştirir.

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

Artık bir işlevi alan bir fonksiyona sahipsiniz ve bir faktoring gibi görünen başka bir işlevi döndürüyorsunuz, ancak kendini çağırmak yerine, dış fonksiyona aktarılan argümanı çağırıyor. Bunu faktoring nasıl yapıyorsunuz? İçsel işlevi kendisine iletin. Y-Birleştirici bunu, özyinelemeyi tanıtabilecek kalıcı bir isme sahip bir fonksiyon olarak yapar.

// 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.
}

Faktöriyelinin kendisinden ziyade, olan şey faktörlüğün faktörel jeneratörü çağırmasıdır (özyinelemeli çağrı tarafından Y-Combinator'a döndürülür). Ve t'nin akım değerine bağlı olarak, jeneratörden döndürülen fonksiyon, jeneratörü tekrar çağırır, t - 1 ile veya sadece 1'i geri döndürür, özyinlemeyi sonlandırır.

Karmaşık ve şifreli, ancak çalışma zamanında her şey sallanıyor ve çalışmasının anahtarı "ertelenmiş yürütme" ve iki işlevi yaymak için özyinelemenin parçalanması. İç F, bir sonraki yinelemede çağrılmak üzere argümanı (- === -) geçirilir, sadece gerekirse .

278
Chris Ammerman

Bunu, http://www.mail-archive.com/[email protected]/msg02716.html 'dan birkaç yıl önce yazdığım bir açıklamadan kaldırdım.

Bu örnekte JavaScript kullanacağım, ancak diğer birçok dilde de işe yarayacak.

Amacımız, sadece 1 değişkenli fonksiyonlar kullanarak ve hiçbir atama olmadan, şeyleri isimle tanımlayarak vb. Kullanarak 1 değişkenli özyinelemeli bir fonksiyon yazabilmektir. (Neden bu bizim amacımız başka bir soru? verildi.) İmkansız görünüyor, ha? Örnek olarak, faktöriyeliyi uygulayalım.

Peki, adım 1 biraz aldattığımızda bunu kolayca yapabileceğimizi söylemek. 2 değişken ve atama işlevlerini kullanarak en azından özyinelemeyi ayarlamak için atama kullanmak zorunda kalmayabiliriz.

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

Şimdi bakalım daha az hile yapabilir miyiz? Öncelikle ödev kullanıyoruz, ancak buna ihtiyacımız yok. Sadece X ve Y'yi satır içi yazabiliriz.

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

Fakat 1 değişkenli bir fonksiyon elde etmek için 2 değişkenli fonksiyonları kullanıyoruz. Düzeltebilir miyiz? Haskell Curry adında akıllı bir adam düzgün bir numaraya sahiptir, eğer daha iyi fonksiyonlara sahipseniz, sadece 1 değişkenli fonksiyonlara ihtiyacınız olur. Kanıt, 2 (veya genel durumda daha fazla) değişken fonksiyonlarından tamamen değişken bir mekanik metin dönüşümü ile 1 değişkene kadar alabileceğinizdir:

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

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

nerede ... tamamen aynı kalır. (Bu numara, mucitten sonra "körleme" olarak adlandırılır. Haskell dili Haskell Curry için de adlandırılır. İşe yaramaz ıvır zıvır şeyler altındaki dosya.) Şimdi bu dönüşümü her yere uygulayın ve son halimizi aldık.

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

Denemek için çekinmeyin. Uyarı (), o, ne olursa olsun, bir düğmeye bağlayın. Bu kod, iki değişkeni atamadan, bildirmeden veya fonksiyonlarını kullanmadan özyinelemeli olarak hesaplar. (Ancak, nasıl çalıştığını izlemeye çalışmak, başınızı döndürmek için muhtemeldir. Ve türetmeksizin, sadece hafifçe yeniden biçimlendirilmiş olarak teslim etmek, karıştırmak ve karıştırmak için kesin bir kodla sonuçlanacaktır.)

Faktörleri özyinelemeli olarak tanımlayan 4 satırı, istediğiniz diğer özyinelemeli işlevle değiştirebilirsiniz.

98
btilly

Acaba bunu temelden inşa etmeye çalışmanın bir yararı var mı merak ediyorum. Bakalım. İşte temel, özyinelemeli faktoring işlevi:

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

Refactor yapalım ve hesaplamayı yapmak yerine adsız bir faktörel hesaplama fonksiyonunu döndüren fact adlı yeni bir fonksiyon yaratalım:

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

var factorial = fact();

Bu biraz garip ama bunda yanlış bir şey yok. Sadece her adımda yeni bir faktör işlevi yaratıyoruz.

Bu aşamada özyineleme hala oldukça açık. fact işlevinin kendi adının farkında olması gerekir. Özyinelemeli aramayı değiştirelim:

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

Bu harika, ama recurser hala kendi adını bilmeli. Bunu da parametreleştirelim:

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

var factorial = recurser(recurser);

Şimdi, doğrudan recurser(recurser) öğesini çağırmak yerine, sonucunu döndüren bir sarmalayıcı işlevi oluşturalım:

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

var factorial = Y();

Şimdi recurser adından tamamen kurtulabiliriz; bu sadece Y'nin iç işlevine bir argümandır; bu işlev, işlevle değiştirilebilir.

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

var factorial = Y();

Halen başvuruda bulunulan tek harici ad fact, ancak şimdiye kadar kolayca değiştirilebildiği, tam ve genel bir çözüm oluşturduğu açıkça görülmeli:

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);
    };
});
80
Wayne Burkett

Yukarıdaki cevapların çoğu, Y-birleştiricinin ne olduğunu açıklamaktadır is ama ne olduğu for .

Sabit nokta birleştiricileri , lambda matematiği 'in turing tamamlandı olduğunu göstermek için kullanılır. Bu hesaplama teorisinde çok önemli bir sonuçtur ve işlevsel programlama için teorik bir temel sağlar.

Sabit nokta birleştiricileri çalışmak da gerçekten işlevsel programlamayı anlamama yardımcı oldu. Ancak gerçek programlamada onlar için hiç bir kullanım bulamadım.

48
Jørgen Fogh

y-birleştirici JavaScript :

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

Düzen : Kodlara bakmaktan çok şey öğreniyorum, ancak bu biraz arka plan olmadan yutmak biraz zor - bunun için üzgünüm. Diğer cevaplar tarafından sunulan bazı genel bilgiler sayesinde, olanları ayırmaya başlayabilirsiniz.

Y işlevi "y-birleştirici" dir. Şimdi Y'nin kullanıldığı var factorial satırına bir göz atın. İç işlevinde daha sonra da kullanılan bir parametreye (bu örnekte, recurse) sahip bir işlevi ilettiğinize dikkat edin. Parametre ismi temel olarak özyinelemeli bir çağrı yapmasına izin veren içsel fonksiyonun adı olur (tanımında recurse() kullandığı için.) işlevin Y'ye geçti.

Y'nin sihri nasıl yaptığının tam açıklaması için, bağlantılı makale (benim tarafımdan değil btw.) 'I inceleyin.

23
Zach

Fonksiyonel programlamaya derinlemesine rastlamayan ve şimdi başlamayı umursamayan, ancak biraz meraklı olan programcılar için:

Y Birleştirici, işlevlerin isimlerinin olmadığı ancak değişkenler olarak geçirilebileceği, geri dönüş değerleri olarak kullanılabileceği ve diğer işlevler içinde tanımlandığı durumlarda özyinelemeyi uygulamanıza izin veren bir formüldür.

Fonksiyonu kendisine bir argüman olarak ileterek çalışır, böylece kendisini çağırabilir.

Gerçekten matematik olan ancak etkili bir programlama dili olan lambda hesabının bir parçası ve bilgisayar bilimleri ve özellikle de işlevsel programlama için oldukça temel.

Programlama dilleri fonksiyonları isimlendirmenize izin verme eğiliminde olduğundan, Y Kombinatorünün günlük pratik değeri sınırlıdır.

Polis kadrosunda tanımlamanız gerekiyorsa, şuna benzer:

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

Tekrarlanan (λx.f (x x)) nedeniyle genellikle tespit edebilirsiniz.

λ sembolleri, lambda hesabına ismini veren Yunanca lambda harfidir ve çok fazla (λx.t) tarzı terim vardır çünkü lambda hesabının görünüşü budur.

17
El Zorko

Diğer cevaplar, önemli bir gerçeği olmadan, buna kısa ve öz bir cevap verir: Sabit nokta birleştiriciyi herhangi bir pratik dilde bu kıvrımlı şekilde uygulamanıza gerek yoktur ve bunu yapmak pratik bir amaca hizmet etmez ("bak, ne y-birleştiricisini biliyorum dır-dir"). Önemli bir teorik kavramdır, ancak pratikte değeri yoktur.

11
Ales Hakl

Y-Combinator ve Factorial işlevinin bir JavaScript uygulamasıdır (Douglas Crockford'un makalesinde, şu adreste bulunabilir: 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
xgMz

Y-Combinator, akı kapasitörünün başka bir adıdır.

6
Jon Davis

Anonim özyineleme

Sabit noktalı bir birleştirici, tanım gereği denkliği sağlayan yüksek dereceli bir işlevdir fix

forall f.  fix f  =  f (fix f)

fix f, sabit nokta denklemine bir çözümü x temsil eder

               x  =  f x

Doğal sayının faktörü, ispat edilebilir

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

fix kullanarak, genel/urs özyinelemeli işlevler üzerindeki isteğe bağlı yapılı deliller, adsız öz-referans olmadan yapılabilir.

fact n = (fix fact') n

nerede

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

öyle ki

   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

Bu resmi kanıt ki

fact 3  =  6

rewrites için sabit nokta birleştirici denkliğini metodik olarak kullanır.

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

Lambda hesabı

Türlenmemiş lambda hesabı formalizmi, bağlamsız bir gramerden oluşur.

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

v, beta ve eta azaltma kurallar

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

Beta indirgeme, x değişkeninin tüm serbest oluşumlarını (“işlev”) gövdesindeki B ifadesinde (“argüman”) E ifadesiyle değiştirir. Eta azaltma gereksiz soyutlamayı ortadan kaldırır. Bu bazen biçimcilikten çıkarılmıştır. İndirgeme kuralının uygulanmadığı indirgenemez ifadesi normalde normaldir veya kanonik biçim .

λ x y. E

kısaca

λ x. λ y. E

(soyutlama çokluğu),

E F G

kısaca

(E F) G

(uygulama sol ilişkilendirme),

λ x. x

ve

λ y. y

alfa eşdeğeri .

Soyutlama ve uygulama lambda hesabının sadece iki dil ilkesidir, ancak rasgele karmaşık verilerin ve işlemlerin kodlanmasına izin verir .

Kilise rakamları, Peano-axiomatic naturals'e benzer doğal sayıların bir kodudur.

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

Resmi bir kanıt

1 + 2  =  3

beta azaltma yeniden yazma kuralını kullanarak:

   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

Combinators

Lamda matematiğinde birleştiriciler serbest değişken içermeyen soyutlamalardır. En basit haliyle: I, kimlik birleştiricisi

λ x. x

kimlik fonksiyonuna izomorfik

id x = x

Bu tür birleştiriciler, SKI sistemi gibi birleştirici hesapların ilkel işletmenleridir.

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

Beta indirgemesi kesinlikle normalleşmiyor ; indirgenebilir ifadelerin tümü değil, “redeksler”, beta indirimi altında normal forma yakınsayabilir. Basit bir örnek omega ω birleştiricinin farklı uygulamasıdır.

λ x. x x

kendisine:

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

En soldaki alt ifadelerin (“kafalar”) azaltılması önceliklidir. Uygulamalı düzen , ikame öncesi argümanları normalleştirir, normal düzen yapmaz. İki strateji, istekli değerlendirmeye benzer; C ve tembel değerlendirme, örn. Haskell.

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

istekli uygulama sırası beta indirimi altında ayrılıyor

=  (λ 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))
. . .
=  _|_

beri katı anlambilim

forall f.  f _|_  =  _|_

ancak tembel normal sipariş beta azaltma altında yakınsamalar

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

Bir ifadenin normal bir şekli varsa, normal sıradaki beta azaltma onu bulur.

Y

Y sabit nokta birleştiricinin temel özelliği

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

tarafından verilir

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

Denklik

Y g  =  g (Y g)

izomorfiktir

fix f  =  f (fix f)

Yazılmamış lambda hesabı, genel/urs-özyinelemeli işlevler üzerinde isteğe bağlı yapılı deliller kodlayabilir.

 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

(Çarpma gecikmeli, birleşme)

Kilise yazılmamış lambda matematiği için, Y dışında yinelemeli olarak numaralandırılabilir bir sabit nokta birleştiricilerin var olduğu gösterilmiştir.

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

Normal dereceli beta azaltma, uzamasız türlenmemiş lambda matematiğini Turing-complete yeniden yazma sistemi yapar.

Haskell'de sabit nokta birleştirici zarif bir şekilde uygulanabilir

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

Haskell'in tembelliği, tüm alt ifadeler değerlendirilmeden önce bir inceliğe normalleşir.

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

6
user6428287

Y-Combinator'a hem Clojure'de hem de Scheme'de bir çeşit "aptal kılavuzu" yazdım. "Küçük Schemer" deki materyallerden etkilenirler.

Düzende: https://Gist.github.com/z5h/238891

veya Clojure: https://Gist.github.com/z5h/5102747

Her iki derste de kodlar yorumlarla doludur ve en sevdiğiniz editöre kesilerek yapıştırılabilir.

5
z5h

Y-birleştiricisi adsız özyineleme uygular. Yani yerine

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

yapabilirsin

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

tabii ki, y-birleştirici sadece isim-isim dillerinde çalışır. Bunu herhangi bir normal arama çağrısı dilinde kullanmak istiyorsanız, ilgili z-birleştiricisine ihtiyacınız olacaktır (y-birleştirici ayrışır/sonsuz döngüye).

4
Andrew

Bu operatör hayatınızı kolaylaştırabilir:

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

Sonra ekstra fonksiyondan kaçınırsınız:

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

Sonunda, fac(5) olarak adlandırılır.

3
Tires

Sabit nokta birleştiricisi (veya sabit nokta operatörü), diğer fonksiyonların sabit bir noktasını hesaplayan daha yüksek dereceli bir fonksiyondur. Bu işlem, programlama dili teorisiyle ilgilidir çünkü dilin çalışma zamanı motorundan açık bir destek olmadan, özyinelemenin yeniden yazma kuralı biçiminde uygulanmasına izin verir. (src Vikipedi)

3
Thomas Wagner

Bence bunu cevaplamanın en iyi yolu, JavaScript gibi bir dil seçmek.

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

Şimdi tekrar yazınız, böylece fonksiyon içindeki fonksiyonun ismini kullanmaz, fakat yine de yinelemeli olarak çağırır.

factorial işlev isminin görülmesi gereken tek yer çağrı sitesinde.

İpucu: işlev adlarını kullanamazsınız, ancak parametre adlarını kullanabilirsiniz.

Sorunu çalış. Bakma. Çözdükten sonra, y-birleştiricinin hangi sorunu çözdüğünü anlayacaksınız.

0
zumalifeguard