Jeśli moja funkcja spełnia poniżej dwóch wymagań, uważam, że funkcja Sum
zwraca sumę pozycji na liście, na której pozycja ocenia się na prawdę, jeśli dany warunek można uznać za funkcję czystą, prawda?
1) Dla danego zestawu i / p zwracane jest to samo o / p, niezależnie od czasu wywołania funkcji
2) Nie ma żadnych skutków ubocznych
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;
}
Przykład: Sum(x=>x%2==0, new List<int> {1,2,3,4,5...100});
Powód, dla którego zadaję to pytanie, wynika z tego, że prawie wszędzie widzę ludzi, którzy powinni unikać operatora przypisania i pętli, ponieważ jest to konieczny styl programowania. Co więc może pójść nie tak z powyższym przykładem, który wykorzystuje pętle i operator przypisania w kontekście programowania funkcji?
c#
functional-programming
imperative-programming
pure-function
rahulaga_dev
źródło
źródło
item
zmienna zmutowana w pętli.Odpowiedzi:
Co w programowaniu funkcjonalnym robi różnicę?
Programowanie funkcjonalne jest z zasady deklaratywne . Mówisz, jaki jest twój wynik, a nie jak go obliczyć.
Rzućmy okiem na naprawdę funkcjonalną implementację twojego fragmentu kodu. W Haskell byłoby to:
Czy jasne jest, jaki jest wynik? Jest to suma liczb spełniających orzeczenie. Jak to jest obliczane? Nie obchodzi mnie to, zapytaj kompilatora.
Można powiedzieć, że używanie
sum
ifilter
jest podstępem i nie ma znaczenia. Pozwól to wdrożyć bez tych pomocników (chociaż najlepszym sposobem byłoby ich wdrożenie w pierwszej kolejności).Rozwiązanie „Functional Programming 101”, które nie korzysta
sum
z rekurencji:Nadal jest całkiem jasne, jaki jest wynik pod względem wywołania jednej funkcji. Jest albo
0
, alborecursive call + h or 0
, w zależności odpred h
. Nadal dość proste, nawet jeśli wynik końcowy nie jest od razu oczywisty (choć przy odrobinie praktyki to naprawdę brzmi jakfor
pętla).Porównaj to ze swoją wersją:
Jaki jest wynik? O, widzę: pojedynczy
return
rachunek, nie zaskakuje tutaj:return result
.Ale co to jest
result
?int result = 0
? To nie wydaje się właściwe. Zrobisz coś później0
. Ok, dodajeszitem
s. I tak dalej.Oczywiście dla większości programistów jest to dość oczywiste, co dzieje się w takiej prostej funkcji, ale dodaj jakieś dodatkowe
return
stwierdzenie i nagle trudniej będzie je wyśledzić. Cały kod dotyczy tego , jak i co pozostało czytelnikowi, aby się zorientować - jest to zdecydowanie bardzo imperatywny styl .Czy zmienne i pętle są nieprawidłowe?
Nie.
Istnieje wiele rzeczy, które są o wiele łatwiejsze do wyjaśnienia, i wiele algorytmów, które wymagają zmiennego stanu, aby były szybkie. Ale zmienne są z natury bezwzględnie konieczne, wyjaśniając, jak zamiast tego , i dając niewielkie przewidywanie, jaka może być ich wartość, kilka linii później lub po kilku iteracjach pętli. Pętle generalnie wymagają, aby państwo miało sens, dlatego też są z natury bezwzględnie konieczne.
Zmienne i pętle nie są po prostu programowaniem funkcjonalnym.
Podsumowanie
Współczesne programowanie funkcyjne to nieco więcej stylu i użyteczny sposób myślenia niż paradygmat. W tym sposobie myślenia zdecydowanie preferowane są czyste funkcje, ale tak naprawdę to tylko niewielka część.
Większość rozpowszechnionych języków pozwala korzystać z niektórych funkcjonalnych konstrukcji. Na przykład w Pythonie możesz wybierać między:
lub
lub
Te wyrażenia funkcjonalne dobrze pasują do tego rodzaju problemów i po prostu skracają kod (a krótszy jest dobry ). Nie powinieneś bezmyślnie zastępować ich imperatywnym kodem, ale gdy pasują, prawie zawsze są lepszym wyborem.
źródło
W programowaniu funkcjonalnym odradza się stosowanie stanu zmiennego. Pętle są w związku z tym zniechęcane, ponieważ pętle są użyteczne tylko w połączeniu ze stanem zmiennym.
Funkcja jako całość jest czysta, co jest świetne, ale paradygmat programowania funkcjonalnego ma zastosowanie nie tylko na poziomie całych funkcji. Chcemy także unikać stanu zmiennego również na poziomie lokalnym, wewnątrz funkcji. Rozumowanie jest w zasadzie takie samo: unikanie stanu zmiennego ułatwia zrozumienie kodu i zapobiega niektórym błędom.
W twoim przypadku możesz napisać,
numbers.Where(predicate).Sum()
co jest znacznie prostsze. A prostsze oznacza mniej błędów.źródło
Where
innumbers.Where(predicate).Sum()
- korzysta zforeach
pętli.Chociaż masz rację, że z punktu widzenia zewnętrznego obserwatora twoja
Sum
funkcja jest czysta, wewnętrzna implementacja wyraźnie nie jest czysta - masz zapisany stan, wresult
którym wielokrotnie mutujesz. Jednym z powodów unikania stanu zmiennego jest to, że powoduje ono większe obciążenie poznawcze programisty, co z kolei prowadzi do większej liczby błędów [potrzebne cytowanie] .Podczas gdy w takim prostym przykładzie, ilość przechowywanego stanu zmiennego jest prawdopodobnie wystarczająco mała, aby nie powodować żadnych poważnych problemów, nadal obowiązuje ogólna zasada. Przykład zabawkowy jak
Sum
prawdopodobnie nie jest najlepszym sposobem zilustrowania przewagi programowania funkcjonalnego nad imperatywnym - spróbuj zrobić coś z wieloma zmiennymi stanami, a zalety mogą stać się wyraźniejsze.źródło