Go'da iç içe geçmiş işlev bildirimlerine izin vermeyerek hafifletilen sorunlar nelerdir?


87

Lambdalar beklendiği gibi çalışır:

func main() {
    inc := func(x int) int { return x+1; }
}

Ancak, bir beyannamenin içinde aşağıdaki beyana izin verilmez:

func main() {
    func inc(x int) int { return x+1; }
}

Hangi nedenle iç içe geçmiş işlevlere izin verilmez?


hmm bunu yapmak isteyip istemediğinizi bilmiyorum func main() { func (x int) int { return x+1; }(3) }
ymg

@YasirG. ama bu da bir lambda, değil mi?
Yorumunu anlamıyorum

sözdizimindeki ikinci örneği etkinleştirmek hangi işlevselliğe izin verir, bu ilk durum tarafından desteklenmez?
Not_a_Golfer

@yannbane bu bir lambda ifadesi, JS gibi başka bir fonksiyonun içinde bir fonksiyon tanımlayabileceğinizi sanmıyorum. Bu yüzden, en uygun seçeneğinizin lambda kullanmak olduğunu söyleyebilirim.
ymg

@Not_a_Golfer: Bir olasılık, JavaScript'in yaptığı gibi uygulamak olabilir; esasen bir değişkene bir işlev atamak, bir işlev bildirmekten çok farklıdır çünkü kontrol akışı bu tür değişkenleri etkilerken JavaScript'teki işlevler etkilenmez. Bu inc(), asıl bildirimden önce ikinci örnekte arayabileceğiniz anlamına gelir . Fakat! Nedenler arıyorum, Go hakkında pek bir şey bilmiyorum ama bu kuralın arkasındaki mantığın ne olduğunu öğrenmek istiyorum.
corazza

Yanıtlar:


54

Bu bariz özelliğe izin verilmemesinin 3 nedeni olduğunu düşünüyorum.

  1. Derleyiciyi biraz karmaşıklaştırır. Derleyici şu anda tüm işlevlerin en üst düzeyde olduğunu bilir.
  2. Bu, yeni bir programcı hatası sınıfına neden olur - bir şeyi yeniden düzenleyebilir ve bazı işlevleri yanlışlıkla iç içe geçirebilirsiniz.
  3. İşlevler ve kapanışlar için farklı bir söz dizimine sahip olmak iyi bir şeydir. Kapatma yapmak, potansiyel olarak bir işlev yapmaktan daha pahalıdır, bu yüzden bunu yaptığınızı bilmelisiniz.

Yine de bunlar sadece benim görüşlerim - dil tasarımcılarından resmi bir açıklama görmedim.


2
Pascal (en azından Delphi'nin enkarnasyonu) onları doğru ve basit hale getirdi: yuvalanmış işlevler tıpkı normal gibi davranır, ancak aynı zamanda çevreleyen işlevin kapsamındaki değişkenlere de erişebilir. Bunların uygulanmasının zor olduğunu düşünmüyorum. Öte yandan, çok sayıda Delphi kodu yazdığım için, iç içe işlevlere çok ihtiyacım olduğundan emin değilim: bazen kendilerini havalı hissediyorlar, ancak çevreleyen işlevi zorlukla okunabilir hale getirme eğilimindeler. Ayrıca ebeveynlerinin argümanlarına erişmek, bu değişkenlere örtük olarak erişildiğinden (biçimsel parametreler olarak aktarılmadığından) programı okumayı zorlaştırabilir.
kostix

1
yerel işlevler, yöntemleri ayıklama yolunda bir ara yeniden düzenleme adımı olarak mükemmeldir. C # 'da, bunları çevreleyen işlevden değişkenleri yakalamasına izin verilmeyen statik yerel işlevleri tanıttıktan sonra daha değerli hale getirdiler, böylece herhangi bir şeyi parametre olarak iletmeye zorlanacaksınız. Statik yerel işlevler, 3. noktayı sorun olmaktan çıkarır. Nokta 2 de benim bakış açıma göre sorun teşkil etmemektedir.
Cosmin Sontu

47

Elbette öyleler. Bunları bir değişkene atamanız yeterlidir:

func main() {
    inc := func(x int) int { return x+1; }
}

5
kayda değer, bu tür işlevleri (inc) özyinelemeli olarak çağıramazsınız.
Mohsin Kale

1
var inc func(int) int
tekrarlı

28

Sıkça Sorulan Sorular (SSS)

Go neden X özelliğine sahip değil?

Her dil yeni özellikler içerir ve birinin en sevdiği özelliği atlar. Go, programlamanın mutluluğu, derleme hızı, kavramların ortogonalitesi ve eşzamanlılık ve çöp toplama gibi özellikleri destekleme ihtiyacı göz önünde bulundurularak tasarlandı. En sevdiğiniz özellik, uymadığından, derleme hızını veya tasarımın netliğini etkilediği veya temel sistem modelini çok zorlaştıracağı için eksik olabilir.

Go'nun X özelliğinin eksik olması sizi rahatsız ediyorsa, lütfen bizi affedin ve Go'nun sahip olduğu özellikleri araştırın. X'in eksikliğini ilginç yollarla telafi ettiklerini görebilirsiniz.

İç içe geçmiş işlevler eklemenin karmaşıklığını ve masrafını ne haklı çıkarır? İç içe geçmiş işlevler olmadan yapamayacağınız ne yapmak istiyorsunuz? Ve benzeri.


19
Adil olmak gerekirse, hiç kimsenin iç içe geçmiş işlevlere izin vermenin neden olacağı herhangi bir karmaşıklık gösterdiğini sanmıyorum. Artı, alıntılanan felsefeye katılsam da, iç içe geçmiş işlevleri bir "özellik" olarak adlandırmanın, bir özellik olarak ihmallerinden bahsetmek kadar mantıklı olduğundan emin değilim. İç içe geçmiş işlevlere izin vermenin izin verebileceği herhangi bir karmaşıklık var mı? Lambdas için sadece sözdizimsel şeker olacaklarını varsayıyorum (başka makul davranışlar düşünemiyorum).
joshlf

Go derlendiğinden, bu AFAIK'i yapmanın tek yolu lambdaları tanımlamak için başka bir sözdizimi yaratacaktır. Ve bunun için gerçekten bir kullanım durumu görmüyorum. gerçek zamanlı olarak oluşturulan bir statik işlev içinde statik bir işleve sahip olamazsınız - ya işlevi tanımlayan belirli bir kod yolunu girmezsek?
Not_a_Golfer

Lambda arayüzünü {} iletin ve assert yazın. Örneğin. f_lambda: = lambda (func () rval {}) veya prototip ne olursa olsun. Func decl sözdizimi bunu desteklemez ama dil tamamen destekler.
BadZen


8

Go'da iç içe geçmiş işlevlere izin verilir. Bunları dış işlevdeki yerel değişkenlere atamanız ve bu değişkenleri kullanarak çağırmanız yeterlidir.

Misal:

func outerFunction(iterations int, s1, s2 string) int {
    someState := 0
    innerFunction := func(param string) int {
        // Could have another nested function here!
        totalLength := 0
        // Note that the iterations parameter is available
        // in the inner function (closure)
        for i := 0; i < iterations; i++) {
            totalLength += len(param)
        }
        return totalLength
    }
    // Now we can call innerFunction() freely
    someState = innerFunction(s1)
    someState += innerFunction(s2)
    return someState
}
myVar := outerFunction(100, "blah", "meh")

İç işlevler genellikle yerel gorutinler için kullanışlıdır:

func outerFunction(...) {
    innerFunction := func(...) {
        ...
    }
    go innerFunction(...)
}

Hareket halindeyken kapatma, bazı yönlerden düz işlevden farklıdır. Örneğin, kapanışı yinelemeli olarak çağıramazsınız.
Michal Zabielski

7
@ MichałZabielski: Tanımlamadan önce bildirirseniz, tekrarlı olarak çağırabilirsiniz.
P Daddy

1

Hemen ()sonuna ekleyerek aramanız yeterlidir.

func main() {
    func inc(x int) int { return x+1; }()
}

Düzenleme: işlev adı olamaz ... bu yüzden hemen çağrılan bir lambda işlevi:

func main() {
    func(x int) int { return x+1; }()
}

1
Uhh bu, bir işlev tanımından
beklenene uymuyor

1
@corazza Ah, soruyu okuduğumda "deklarasyon" kelimesini kaçırdım. Benim hatam.
Nick

1
@corazza Ayrıca sözdizimini de bozdum. İşlev adını kaldırmak için gerekli. Yani, temelde hemen çağrılan bir lambda işlevi.
Nick
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.