Fonksiyonel programlamada, hiçbir yan etkisi olmayan lokal değişken değişkenler hala “kötü uygulama” olarak kabul edilir mi?


23

Yalnızca dahili olarak kullanılan bir fonksiyonda değişken yerel değişkenlere sahip olmak mı (ör. Fonksiyonun hiçbir yan etkisi yoktur, en azından kasıtlı olarak değil) hala "işlevsel olmayan" olarak mı kabul edilir?

Örneğin, "Scala ile fonksiyonel programlama" kurs stilinde herhangi bir varkullanımı kötü olarak kabul eder

Benim sorum, eğer fonksiyonun yan etkisi yoksa, zorunlu stil kodunu yazmak hala önerilmiyor mu?

Örneğin, akümülatör deseni ile kuyruk özyinelemesi kullanmak yerine, döngü için yerel bir işlem yapmak ve yerel bir değişken oluşturmak ListBufferve buna ekleme yapmak için giriş değiştirilmediği sürece yanlış olan ne ?

Cevabı "evet, yan etkileri olmasa bile, her zaman cesaretini kırıyorlarsa" bunun nedeni nedir?


3
Şimdiye dek duyduğum konu hakkındaki tüm tavsiyeler, heyecanlar vb. Ortak değişkenliği karmaşıklığın kaynağı olarak adlandırıyor. Bu kurs sadece yeni başlayanlar tarafından tüketilmek üzere mi planlanıyor? O zaman muhtemelen iyi niyetli bir kasıtlı aşırı basitleştirmedir.
Kilian Foth

3
@KilianFoth: Paylaşılabilir değişken durum, çok iş parçacıklı bağlamlarda bir sorundur, ancak paylaşılmayan değişken durum, programların da mantıklı olması zor olabilir.
Michael Shaw

1
Yerel değişken değişken kullanmanın mutlaka kötü bir uygulama olmadığını düşünüyorum, ancak "işlevsel stil" değil: Sanırım (geçen sonbaharda aldığım) Scala kursunun amacı, size işlevsel bir tarzda programlama öğretmektir. İşlevsel ve zorunlu stil arasında ne zaman net bir şekilde ayrım yapabileceğinizi, ne zaman kullanacağınıza karar verebilirsiniz (programlama dilinin her ikisine de izin veriyorsa). varher zaman işlevsel değildir. Scala, tamamen değişkenlikten kaçınmayı sağlayan tembel vals ve tail recursion optimizasyonuna sahiptir.
Giorgio

Yanıtlar:


17

Buradaki kesin olarak kötü bir uygulama olan şey, bir şeyin değilken saf bir işlev olduğunu iddia etmektir.

Değişken değişkenler gerçekten ve tamamen kendine yetecek şekilde kullanılırsa, işlev harici olarak saftır ve herkes mutludur. Haskell aslında bunu açıkça desteklemektedir , tür sistemi ile değişken referansların onları yaratan fonksiyonun dışında kullanılamayacağından emin olmak da mümkündür.

Bu, "yan etkilerden" bahsetmenin, ona bakmanın en iyi yolu olmadığını düşünüyorum (ve bu yüzden yukarıda "saf" demiştim). İşlev ve dış durum arasında bir bağımlılık yaratan herhangi bir şey, bunun nedenini zorlaştırır ve şimdiki zamanı bilmek ya da gizli değişken durumları net olmayan bir şekilde kullanmak gibi şeyleri içerir.


16

Sorun kendiliğinden değişkenlik değil, referans saydamlığı eksikliği.

Referans niteliğinde saydam bir şey ve ona referans her zaman eşit olmak zorundadır, bu nedenle referans niteliğinde saydam işlev, belirli bir girdi kümesi için her zaman aynı sonuçları döndürür ve referans niteliğinde saydam bir "değişken", bir değişkenden çok bir değerdir, çünkü değişemez. İçinde değişken değişken olan referans olarak saydam bir işlev yapabilirsiniz; bu bir problem değil. Ne yaptığınıza bağlı olarak, fonksiyonun referans olarak şeffaf olmasını sağlamak zor olabilir.

Değişebilirliğin çok fonksiyonel bir şeyi yapmak için nerede kullanılması gerektiğini düşünebileceğim bir örnek var: Notlandırma. Notlandırma, bir işlevdeki değerleri önbelleğe almaktır, bu nedenle yeniden hesaplanmaları gerekmez; referans olarak saydamdır, ancak mutasyon kullanmaktadır.

Ancak genel olarak referanssal şeffaflık ve değişmezlik, referans niteliğindeki saydam bir işlev ve notlandırmadaki yerel değişken değişken dışında bir araya gelir, bunun böyle olmadığı başka örnekler olmadığından emin değilim.


4
Not verme ile ilgili düşünceniz çok iyi. Haskell'in programlama için referans şeffaflığını kuvvetle vurguladığını, ancak tembel değerlendirmenin not verme benzeri davranışının, sahnelerin arkasındaki dil çalışma zamanı tarafından yapılan şaşırtıcı miktarda mutasyon içerdiğini unutmayın.
CA McCann

@CCA McCann: Söyleyeceğiniz şeyin çok önemli olduğunu düşünüyorum: işlevsel bir dilde çalışma zamanı, hesaplamayı optimize etmek için mutasyonu kullanabilir, ancak programcının mutasyonu kullanmasına izin veren dilde bir yapı yoktur. Başka bir örnek, döngü değişkenli bir süre döngüsüdür: Haskell'de, değişken bir değişkenle uygulanabilen bir kuyruk özyinelemeli işlev yazabilirsiniz (yığını kullanmaktan kaçınmak için), ancak programcının gördüğü şey, değişken bir işlev değişkenleri sonrakine çağrı.
Giorgio

@Michael Shaw: "Sorun kendi başına değişkenlik değil, referanssal şeffaflığın eksikliği." Belki özgünlük türlerine sahip olduğunuz Temiz dili aktarabilirsiniz: bunlar değişkenliğe izin verir, ancak yine de referansların şeffaflığını garanti eder.
Giorgio

@ Giorgio: Zaman zaman bahsettiğimi söylesem de, Clean hakkında hiçbir şey bilmiyorum. Belki de araştırmalıyım.
Michael Shaw

@Michael Shaw: Clean hakkında çok fazla bilgim yok, ancak referans şeffaflığı sağlamak için benzersizlik türleri kullandığını biliyorum. Temel olarak, değişiklikten sonra eski değere atıfta bulunmamak koşuluyla bir veri nesnesini değiştirebilirsiniz. IMO bu noktanızı açıklar: referans şeffaflığı en önemli noktadır ve değişmezlik bunu sağlamanın yalnızca bir yoludur.
Giorgio

8

Bunu, "kötü uygulama" ya da "iyi uygulama" yla kaynatmak pek de iyi değil. Scala değişken değerleri destekler, çünkü belirli sorunları değişken değerlerden çok daha iyi çözer, yani doğada yinelemeli olanlar.

Perspektif açısından, CanBuildFromscala tarafından sağlanan hemen hemen tüm değişmez yapılar aracılığıyla dahili olarak bir çeşit mutasyon olduğundan eminim . Mesele şu ki ortaya çıkardıkları şey değişken değil. Olabildiğince çok değerin değişmemesi, programın nedenini kolaylaştıracak ve hataya daha az açık hale getirecektir .

Bu, değişkenliğe daha uygun bir probleminiz olduğunda, dahili olarak değişken yapıları ve değerleri dahili olarak sakınmanız gerektiği anlamına gelmez.

Bunu göz önünde bulundurarak, tipik olarak değişken değişkenler (döngü gibi) gerektiren birçok sorun, Scala gibi dillerin (harita / filtre / katlama) sağladığı birçok üst düzey işlevle daha iyi çözülebilir. Bunların farkında olun.


2
Evet, Scala'nın koleksiyonlarını kullanırken neredeyse hiçbir zaman bir forma ihtiyacım yok. map, filter, foldLeftVe forEach çoğu zaman hile yapmak, ancak bunu yaptıklarında değil, ben kaba kuvvet zorunlu koduna döndürülmesine "OK" olduğumu hissediyorum edememek güzel. (elbette yan etkileri olmadığı sürece)
Eran Medan

3

İplik güvenliği ile ilgili olası sorunların yanı sıra, genellikle çok fazla türde güvenlik kaybedersiniz. Emir döngüleri geri dönüş türüne sahiptir Unitve girdiler için hemen hemen her türlü ifadeyi alabilir. Yüksek dereceli fonksiyonlar ve hatta özyinelemede çok daha kesin bir anlamsallık ve tür vardır.

Ayrıca, işlevsel konteyner işleme için zorunlu döngülerden çok daha fazla seçeneğiniz var. Zorunluluk ile, temelde var for, whileve bu gibi ikisine küçük varyasyonlar do...whileve foreach.

İşlevsel olarak, toplanacak, say, süz, bul, flatMap, katla, groupBy, lastIndexWhere, map, maxBy, minBy, partition, scan, sortBy, sortWith, span ve takeWhile öğelerine sahip olursunuz standart kütüphane Bunları elde etmeye alışınca, zorunlu fordöngüler kıyaslamada çok basit görünüyor.

Yerel değişkenliği kullanmanın tek gerçek nedeni performans için çok nadirdir.


2

Çoğunlukla iyi olduğunu söyleyebilirim. Dahası, yapıların bu şekilde üretilmesi, bazı durumlarda performansları iyileştirmek için iyi bir yol olabilir. Clojure, geçici veri yapıları sağlayarak bu sorunu çözmüştür .

Temel fikir, yerel mutasyonlara sınırlı bir kapsamda izin vermek ve daha sonra geri döndürmeden önce yapıyı dondurmaktır. Bu sayede, kullanıcı kodunuz hakkında hala safmış gibi düşünebilir, ancak gerektiğinde yerinde dönüşümler yapabilirsiniz.

Linkin dediği gibi:

Bir ağaç ormana düşerse, ses çıkarır mı? Saf bir işlev değişmez bir dönüş değeri üretmek için bazı yerel verileri değiştirirse, tamam mı?


2

Yerel değişken değişkenlere sahip olmamak bir avantaja sahiptir - işlevi ipliklere karşı daha kolay hale getirir.

Düşük olasılıklı bir veri bozulmasına neden olan böyle bir yerel değişken (kodumda değil, kaynağımda da yok) bulundum. İplik güvenliği bir şekilde veya başka şekilde söylenmedi, çağrılar arasında süren bir durum yoktu ve hiçbir yan etkisi yoktu. İplik güvenli olamayacağımı fark etmedim, 100.000 rastgele veri bozulmasından birini takip etmek bir kraliyet acısı.

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.