İşlevsel programlamada atama operatörü veya döngülerin kullanımı neden önerilmez?


9

İşlevim iki gereksinimin altındaysa, Sum verilen koşul için öğenin doğru olarak değerlendirildiği bir listedeki öğelerin toplamını döndürmek işlevinin saf işlev olarak nitelendirilebileceğine inanıyorum, değil mi?

1) Verilen i / p seti için, fonksiyon çağrıldığı zamandan bağımsız olarak aynı o / p döndürülür

2) herhangi bir yan etkisi yoktur

public int Sum(Func<int,bool> predicate, IEnumerable<int> numbers){
    int result = 0;
    foreach(var item in numbers)
        if(predicate(item)) result += item;
    return result;
}

Misal : Sum(x=>x%2==0, new List<int> {1,2,3,4,5...100});

Bu soruyu sormamın nedeni, atama operatörü ve döngülerden kaçınmayı öneren hemen hemen her yeri zorunlu bir programlama tarzı olduğu için görüyorum. Öyleyse fonksiyon programlama bağlamında döngüler ve atama operatörünü kullanan yukarıdaki örnekte yanlış olan ne olabilir?


1
Herhangi bir yan etkisi yoktur - itemdöngüde mutasyon geçirdiğinde yan etkisi vardır .
Fabio

@Fabio tamam. Ancak yan etki kapsamı hakkında ayrıntılı bilgi verebilir misiniz?
rahulaga_dev

Yanıtlar:


16

İşlevsel programlamada fark yaratan nedir?

İşlevsel programlama ilke olarak bildiricidir . Söyleyecek Ne senin sonuç yerine ise nasıl bunu hesaplamak için.

Snippet'inizin gerçekten işlevsel uygulanmasına bir göz atalım. Haskell'de şöyle olurdu:

predsum pred numbers = sum (filter pred numbers)

Sonucun ne olduğu açık mı ? Oldukça, yüklemi karşılayan sayıların toplamıdır. Nasıl hesaplanır? Umurumda değil, derleyiciye sor.

Muhtemelen sumvefilter işlevini bir hile önemli değildir. O zaman bu yardımcılar olmadan uygulayalım (en iyi yol önce onları uygulamak olsa da).

Kullanılmayan "Fonksiyonel Programlama 101" çözümü sumözyineleme ile:

sum pred list = 
    case list of
        [] -> 0
        h:t -> if pred h then h + sum pred t
                         else sum pred t

Tek işlev çağrısı açısından sonucun ne olduğu hala açıktır . Ya 0, ya da recursive call + h or 0bağlı pred h. Sonuç oldukça hızlı olmasa bile yine de oldukça düzdür (biraz pratikle bu gerçekten bir fordöngü gibi okunur ).

Bunu sürümünüzle karşılaştırın:

public int Sum(Func<int,bool> predicate, IEnumerable<int> numbers){
    int result = 0;
    foreach(var item in numbers)
        if (predicate(item)) result += item;
    return result;
}

Sonuç nedir? Anlıyorum: Tek returnaçıklamada, buraya şaşırtır: return result.

Ama nedir result? int result = 0? Doğru görünmüyor. Daha sonra bununla bir şey yapıyorsun 0. Tamam, sen ekleitem ona s . Ve bunun gibi.

Tabii ki, çoğu programcı için bu, böyle basit bir funciton'da neler olduğu oldukça açıktır, ancak ekstra bir returnifade ekler ve aniden izlemek zorlaşır. Tüm kod, okuyucunun anlaması için nasıl ve ne bırakılacağıyla ilgilidir - bu açıkça çok zorunlu bir stildir .

Değişkenler ve döngüler yanlış mı?

Hayır.

Onlar tarafından daha kolay açıklanabilecek birçok şey ve değişebilir durumun hızlı olmasını gerektiren birçok algoritma vardır. Ancak değişkenler doğası gereği zorunludur, neyin yerine nasıl olduğunu açıklar ve birkaç satır sonra veya birkaç döngü yinelemesinden sonra değerlerinin ne olabileceğine dair çok az tahmin verir. Döngüler genellikle mantıklı olmak için devlete ihtiyaç duyarlar ve bu nedenle de doğası gereği zorunludurlar.

Değişkenler ve döngüler basitçe işlevsel programlama değildir.

özet

Davranışsal olarak klasik programlama, bir paradigmadan biraz daha fazla stil ve yararlı bir düşünme şeklidir. Saf işlevlerin güçlü tercihi bu düşünce yapısındadır, ancak aslında sadece küçük bir kısmıdır.

Çoğu yaygın dil bazı fonksiyonel yapıları kullanmanıza izin verir. Örneğin Python'da aşağıdakiler arasından seçim yapabilirsiniz:

result = 0
for num in numbers:
    if pred(result):
        result += num
return result

veya

return sum(filter(pred, numbers))

veya

return sum(n for n in numbers if pred(n))

Bu işlevsel ifadeler bu tür problemlere iyi uyum sağlar ve sadece kodu kısaltır (ve daha kısa iyidir ). Düşüncesizce kodu onlarla değiştirmemelisiniz, ancak uyduklarında neredeyse her zaman daha iyi bir seçimdir.


güzel açıklama için teşekkürler !!
rahulaga_dev

1
@RahulAgarwal Bu yanıtı ilginç bulabilirsiniz , gerçekleri oluşturmak ve adımları tanımlamak için teğetsel bir kavramı güzel bir şekilde yakalar. Ben de "zorunlu diller yan etkileri içerirken zorunlu diller yok" ifadesini de seviyorum - genellikle fonksiyonel programların dış dünya ile uğraşan durum kodu (veya bazı optimize edilmiş algoritma yürütme) ve tamamen fonksiyonel kod arasında temiz ve çok görünür kesimler var.
Frax

1
@Frax: teşekkürler !! Ben içine bakacağım. Ayrıca son zamanlarda Rich Hickey konuştu Değerlerin değeri Gerçekten parlak. Bence bir başparmak kuralı - "değer tutan bir şey ile çalışmak yerine değerler ve ifade ile çalışmak ve değişebilir"
rahulaga_dev

1
@Frax: Ayrıca FP'nin zorunlu programlama üzerinde bir soyutlama olduğunu söylemek de adil - çünkü sonuçta birisi makineye "nasıl yapılır" talimatını vermek zorunda, değil mi? Evet ise, zorunlu programlama FP ile karşılaştırıldığında daha düşük seviye kontrole sahip değil midir?
rahulaga_dev

1
@Frax: Rahul ile, zorunlu makinenin altta yatan makineye daha yakın olması açısından daha düşük bir seviye olduğu konusunda hemfikirim. Donanım, verilerin kopyalarını ücretsiz olarak oluşturabilseydi, verimliliği artırmak için yıkıcı güncellemelere ihtiyacımız olmayacaktı. Bu anlamda, zorunlu paradigma metale daha yakındır.
Giorgio

9

Değişken durumun kullanımı genellikle fonksiyonel programlamada önerilmez. Sonuç olarak döngüler önerilmez, çünkü döngüler yalnızca değişebilir durumla birlikte kullanışlıdır.

Bir bütün olarak işlev saftır, bu harika, ancak fonksiyonel programlama paradigması sadece tüm işlevler düzeyinde geçerli değildir. Ayrıca yerel düzeyde, iç fonksiyonlarda da değişebilir durumdan kaçınmak istersiniz. Ve akıl yürütme temel olarak aynıdır: Değişken durumdan kaçınmak, kodun anlaşılmasını kolaylaştırır ve bazı hataları önler.

Sizin durumunuzda, numbers.Where(predicate).Sum()çok daha basit olan yazabilirsiniz . Ve daha basit, daha az hata anlamına gelir.


Teşekkür !! Çarpıcı çizgiyi kaçırdığımı düşünüyorum - ancak fonksiyonel programlama paradigması sadece tüm fonksiyonlar düzeyinde geçerli değil, aynı zamanda bu sınırı nasıl görselleştireceğimizi de merak ediyorum. Temel olarak tüketici bakış açısından saf bir işlevdir, ancak aslında bu işlevi yazan geliştirici saf işlev yönergelerine uymamıştır? karıştı :(
rahulaga_dev

@RahulAgarwal: Ne sınırı?
JacquesB

Programlama paradigmasının işlevler açısından tüketici olarak FP olmaya uygun olup olmadığı konusunda kafam karıştı mı? İ uygulanması uygulanması bakarsak bcoz Whereiçinde numbers.Where(predicate).Sum()- bu yararlanır foreachdöngü.
rahulaga_dev

3
@RahulAgarwal: Bir fonksiyonun tüketicisi olarak, bir fonksiyonun veya modülün harici olarak saf olduğu sürece dahili olarak değiştirilebilir durumu kullanıp kullanmadığını umursamazsınız.
JacquesB

7

Harici bir gözlemcinin bakış açısından, Sumfonksiyonunuzun saf olduğunu doğrularken, dahili uygulama açıkça saf değildir - resulttekrar tekrar mutasyon yaptığınız bir durumunuz vardır . Değişebilir durumdan kaçınmanın nedenlerinden biri, programlayıcı üzerinde daha büyük bir bilişsel yük üretmesidir, bu da daha fazla hataya neden olur [alıntı gerekli] .

Bunun gibi basit bir örnekte, depolanan değişken durumun miktarı, ciddi sorunlara neden olmayacak kadar küçük olsa da, genel prensip hala geçerlidir. SumMuhtemelen fonksiyonel bir programlamanın avantajını zorunlu olarak göstermenin en iyi yolu gibi bir oyuncak örneği - çok değişebilir bir duruma sahip bir şey yapmayı deneyin ve avantajlar daha açık hale gelebilir.

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.