Kombinatorler nedir ve programlama projelerine nasıl uygulanır? (pratik açıklama)


51

Kombinatorler nedir?

Arıyorum:

  • pratik bir açıklama
  • nasıl kullanıldıklarına örnekler
  • birleştiricilerin kodun kalitesini / genelliğini nasıl iyileştirdiğini gösteren örnekler

Ben aramıyorum:

  • iş yapmama yardımcı olmayan kombinasyonların açıklamaları (Y-birleştiricisi gibi)

Kombinatorler "zarflara" benzer, fonksiyonlara giren fonksiyonlar diğer fonksiyonlara döner. Değişkenler arasında ihtiyacınız olmadığından kod çoğaltmasının kaldırılmasına yardımcı olabilirler. Bazı yararlı olanlar iki kere (f) = \ x -> f (f (x)), çevir (op) -> \ xy -> y op x, (.) (Fg) x = f (g (x) 'deki gibi )), ($) haritaya yardımcı olabilir (ek olarak <$> olarak adlandırılır) (5 $) gibi <$> [(+1), (* 2)] = [6, 10], köri Lisp’te kullanılabilir / Kısmi uygulama için Python / JavaScript ve unkurry, Haskell'de kayıt gerektiren fonksiyonlar için kullanılabilir. X |> f = fa olduğunda, x |> (uzunluk &&& toplam) |> uncurry (/) ortalamadır.
aoeu256

Yanıtlar:


51

Pratik bir bakış açısından, kombinatorler, mantık parçalarını ilginç ve sıklıkla gelişmiş şekillerde bir araya getirmenize izin veren bir tür programlama yapılarıdır. Genellikle bunları kullanmak, yürütülebilir kodu, genellikle (tarihsel nedenlerle) lambda işlevleri veya lambda ifadeleri adı verilen nesnelere paketleyebilme olasılığına bağlıdır, ancak kilometreniz değişebilir.

(Yararlı) bir birleştiricinin basit bir örneği, parametresiz iki lambda fonksiyonunu alan ve bunları sırayla çalıştıran yeni bir tane yaratır. Gerçek birleştirici şu şekilde genel sözde kodda görünüyor:

func in_sequence(first, second):
  lambda ():
    first()
    second()

Bunu bir birleştirici yapan önemli şey, ikinci satırdaki adsız işlevdir (lambda işlevi); Aradığın zaman

a = in_sequence(f, g)

sonuçta ortaya çıkan nesne a, önce f () ve sonra g () işlemlerinin sonucu değildir , ancak daha sonra sırayla f () ve g () komutlarını çalıştırmak için çağırabileceğiniz bir nesnedir:

a() // a is a callable object, i.e. a function without parameters

Benzer şekilde, iki kod bloğunu paralel olarak çalıştıran bir birleştiriciye sahip olabilirsiniz:

func in_parallel(first, second):
  lambda ():
    t1 = start_thread(first)
    t2 = start_thread(second)
    wait(t1)
    wait(t2)

Ve sonra tekrar,

a = in_parallel(f, g)
a()

İşin güzel yanı 'in_parallel' ve 'in_sequence', her ikisi de aynı tür / imzaya sahip birleştiricilerdir, yani ikisi de parametresiz iki işlev nesnesi alır ve yeni bir tane döndürür. Aslında daha sonra gibi şeyler yazabilirsiniz

a = in_sequence(in_parallel(f, g), in_parallel(h, i))

ve beklendiği gibi çalışır.

Temel olarak, birleştiriciler, programınızın kontrol akışını (diğer şeylerin yanı sıra) usule uygun ve esnek bir şekilde oluşturmanıza izin verir. Örneğin, programınızdaki paralelliğini çalıştırmak için in_parallel (..) birleştirici kullanırsanız, bununla ilgili hata ayıklama işlemini in_parallel birleştiricinin kendisinin uygulamasına ekleyebilirsiniz. Daha sonra, programınızın paralellikle ilgili bir hata içerdiğinden şüpheleniyorsanız, aslında sadece in_parallel biçimini yeniden uygulayabilirsiniz:

in_parallel(first, second):
  in_sequence(first, second)

ve bir vuruşla, tüm paralel bölümler sıralı bölümlere dönüştürülmüştür!

Kombinatorler doğru kullanıldığında çok faydalıdır.

Bununla birlikte, Y birleştiricisi, gerçek hayatta gerekli değildir. Kendi kendine özyinelemeli işlevler oluşturmanıza olanak sağlayan bir birleştiricidir ve Y birleştirici olmadan bunları herhangi bir modern dilde kolayca oluşturabilirsiniz.


9

Y-combinator'ü "işi yapmanıza yardımcı olmayacak" bir şey olarak markalamak yanlıştır. Bazı durumlarda çok faydalı buldum. En açık örnek, bazı gömülü tercüman dillerini hızlıca önyüklemeniz gerektiğidir. Eğer ilkel en az sayıda sağlarsanız, yani sequence, select, call, constve bir closure allocation, tam bir keyfi karmaşık dili oluşturmak için zaten yeterlidir. Özyineleme için özel bir desteğe gerek yoktur - sabit nokta birleştiriciden eklenebilir. Aksi takdirde, daha karmaşık ilkellere ihtiyacınız olacaktır.

Kombinatorler için bir başka belirgin durum şaşırtmacadır. SKI hesabına çevrilmiş olan bir kod pratikte okunamaz durumdadır. Bir algoritmanın uygulamasını gizlemeniz gerekiyorsa, birleştiricileri kullanmayı düşünün, işte bir örnek .

Ve elbette, birleştiriciler, işlevsel dilleri uygulamak için önemli bir araçtır. En kolay yaklaşım (yukarıdaki örnekte olduğu gibi) SKI veya eşdeğeri hesap üzerindendir. Süper arıtıcı , diğer bazı uygulamalarda kullanılır. Bu kitap hakkında derinlemesine konuşuyor.

Bu bir şakadır , fakat çok dikkatli okumaya değer bir şakadır, çünkü birçok gizli programlama tekniği ve teorisi burada ele alınmaktadır.


1
@MattFenwick, basit bir gömülü tercümana düşme ihtiyacı çoğu zaman asla beklemeyeceğiniz bir yerde ortaya çıkar. Örneğin, benim durumumda bir iletişim protokolünü genişletmek için tasarlamam gereken bir dildi. Basit IPC yeterli değildi, bu yüzden protokolün çalıştırılabilir olması gerekiyordu.
SK-mantık

Sorunuzla ilgili olarak @MattFenwick: APL veya J'de bazı kodlar yazmayı deneyebilirsiniz, orada birleştirmek önemlidir, bu yüzden bunları doğru şekilde nasıl uygulayabileceğinize dair bir fikir edinebilirsiniz. Ayrıca, puansız
SK-mantık

7

Biraz etrafımı kazarak bir StackOverflow sorusu buldum , “Birleştiriciler” hakkında iyi bir açıklama (matematikçiler için olmayan) bu sorunun kuzeni. Cevaplardan biri işaret Reginald Braithwaite blog, Homoiconic (örneğin kodda, bağdaştırıcılarla birçok yararlı örneklere bağlar K combinator Ruby'nin tarafından uygulanan, Object#tap- Kullanışlı neden örnekleri için sayfayı okuyun yöntemle).

Combinatory Mantık Wikipedia sayfası daha global combinators açıklanır.


Bu, sorumun ikinci kurşun noktasına değiniyor. Örnek için teşekkürler!

1
Bu yazının bazı iyi bağlantıları var ancak aslında soruyu doğrudan cevaplamıyor. Cevaplar kendi başlarına tamamlanmalı ve gerekirse bağlantıları referans olarak kullanmalıdır.
Aaron
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.