Czy posiadanie zmiennych lokalnych zmiennych w funkcji, które są używane tylko wewnętrznie (np. Funkcja nie ma skutków ubocznych, a przynajmniej nie celowo) jest nadal uważane za „niefunkcjonalne”?
np. podczas sprawdzania stylu kursu „Programowanie funkcjonalne ze Scalą” uznaje każde var
użycie za złe
Moje pytanie, jeśli funkcja nie ma skutków ubocznych, czy odradza się pisanie kodu stylu imperatywnego?
np. zamiast używać rekurencji ogona ze wzorem akumulatora, co jest złego w tworzeniu pętli local for i tworzeniu lokalnego mutable ListBuffer
i dodawaniu do niej, dopóki dane wejściowe nie ulegną zmianie?
Jeśli odpowiedź brzmi „tak, zawsze są zniechęcani, nawet jeśli nie ma żadnych skutków ubocznych”, to jaki jest powód?
functional-programming
Eran Medan
źródło
źródło
var
jest zawsze niefunkcjonalny. Scala ma leniwą optymalizację zawrotów i ogonów, co pozwala całkowicie uniknąć zmiennych.Odpowiedzi:
Jedyną rzeczą, która jest tutaj jednoznacznie złą praktyką, jest twierdzenie, że coś jest czystą funkcją, gdy tak nie jest.
Jeśli zmienne zmienne są używane w sposób, który jest naprawdę i całkowicie samowystarczalny, funkcja jest zewnętrznie czysta i wszyscy są szczęśliwi. Haskell w rzeczywistości wyraźnie to popiera , a system typów zapewnia nawet, że zmienne odwołania nie mogą być używane poza funkcją, która je tworzy.
To powiedziawszy, myślę, że mówienie o „skutkach ubocznych” nie jest najlepszym sposobem, aby na to spojrzeć (i dlatego powiedziałem „czysty” powyżej). Wszystko, co stwarza zależność między funkcją a stanem zewnętrznym, sprawia, że trudniej jest o tym myśleć, a także takie rzeczy, jak znajomość aktualnego czasu lub używanie ukrytego stanu zmiennego w sposób nie bezpieczny dla wątków.
źródło
Problemem nie jest sama w sobie zmienność, to brak przejrzystości odniesienia.
Referencyjnie przejrzysta rzecz i odniesienie do niej zawsze muszą być równe, więc referencyjnie przejrzysta funkcja zawsze zwróci te same wyniki dla danego zestawu danych wejściowych, a referencyjnie przezroczysta „zmienna” jest naprawdę wartością, a nie zmienną, ponieważ nie mogę się zmienić. Możesz utworzyć referencyjnie przezroczystą funkcję, która ma wewnątrz zmienną zmienną; to nie jest problem. Jednak może być trudniej zagwarantować, że funkcja jest referencyjnie przezroczysta, w zależności od tego, co robisz.
Myślę o jednym przypadku, w którym można użyć zmienności, aby zrobić coś, co jest bardzo funkcjonalne: zapamiętywanie. Zapamiętywanie to buforowanie wartości z funkcji, więc nie trzeba ich ponownie obliczać; jest referencyjnie przezroczysty, ale wykorzystuje mutację.
Ale ogólnie przejrzystość referencyjna i niezmienność idą w parze, inne niż lokalna zmienna zmienna w referencyjnie przezroczystej funkcji i zapamiętywaniu, nie jestem pewien, czy istnieją inne przykłady, w których tak nie jest.
źródło
Sprowadzanie tego do „dobrej praktyki” kontra „zła praktyka” nie jest zbyt dobre. Scala obsługuje wartości zmienne, ponieważ rozwiązują one niektóre problemy znacznie lepiej niż wartości niezmienne, a mianowicie te, które mają charakter iteracyjny.
Jeśli chodzi o perspektywę, jestem całkiem pewien, że poprzez
CanBuildFrom
prawie wszystkie niezmienne struktury dostarczone przez scala dokonują wewnętrznej mutacji. Chodzi o to, że to, co ujawniają, jest niezmienne. Zachowanie jak największej liczby wartości niezmiennych pozwala na łatwiejsze uzasadnienie programu i zmniejszenie podatności na błędy .Nie oznacza to, że koniecznie musisz wewnętrznie unikać struktur i wartości, które można modyfikować, gdy masz problem, który lepiej odpowiada zmienności.
Mając to na uwadze, wiele problemów, które zwykle wymagają zmiennych zmiennych (takich jak zapętlanie), można lepiej rozwiązać za pomocą wielu funkcji wyższego rzędu, które zapewniają języki takie jak Scala (mapa / filtr / fold). Bądź tego świadomy.
źródło
map
,filter
,foldLeft
IforEach
rade przez większość czasu, ale kiedy nie, jest w stanie poczuć, że jestem „OK” w celu powrotu do brute force kod imperatyw jest miły. (o ile oczywiście nie występują żadne skutki uboczne)Oprócz potencjalnych problemów z bezpieczeństwem wątków, zwykle tracisz także wiele bezpieczeństwa typu. Pętle imperatywne mają typ zwracany
Unit
i mogą przyjmować dowolne wyrażenie dla danych wejściowych. Funkcje wyższego rzędu, a nawet rekurencja mają znacznie bardziej precyzyjną semantykę i typy.Masz również o wiele więcej opcji funkcjonalnego przetwarzania kontenera niż w przypadku pętli imperatywnych. Z imperatywu, zasadniczo trzeba
for
,while
i wariacje na temat tych dwóch drobnych jakdo...while
iforeach
.W trybie funkcjonalnym masz agregację, zliczanie, filtrowanie, znajdowanie, flatMap, fold, groupBy, lastIndexWhere, map, maxBy, minBy, partycjonowanie, skanowanie, sortowanie, sortowanie, przęsło i takeWhile, aby wymienić tylko kilka bardziej popularnych z Scali standardowa biblioteka. Kiedy przyzwyczaisz się do ich dostępności, imperatywne
for
pętle wydają się zbyt proste w porównaniu.Jedynym prawdziwym powodem użycia lokalnej zmienności jest bardzo rzadko wydajność.
źródło
Powiedziałbym, że w większości jest ok. Co więcej, generowanie struktur w ten sposób może być dobrym sposobem na poprawę wydajności w niektórych przypadkach. Clojure rozwiązał ten problem, udostępniając struktury danych przejściowych .
Podstawową ideą jest dopuszczenie lokalnych mutacji w ograniczonym zakresie, a następnie zamrożenie struktury przed jej zwróceniem. W ten sposób użytkownik może nadal myśleć o kodzie, jakby był czysty, ale możesz przeprowadzać transformacje w miejscu, gdy jest to konieczne.
Jak mówi link:
źródło
Brak lokalnych zmiennych zmiennych ma jedną zaletę - sprawia, że funkcja jest bardziej przyjazna dla wątków.
Zostałem spalony przez taką zmienną lokalną (nie w moim kodzie, ani nie miałem źródła), co spowodowało uszkodzenie danych o niskim prawdopodobieństwie. Bezpieczeństwo wątków nie zostało wspomniane w ten czy inny sposób, nie było żadnego stanu, który utrzymywał się podczas połączeń i nie było żadnych skutków ubocznych. Nie przyszło mi do głowy, że to może nie być bezpieczne dla wątków, ściganie 1 na 100 000 przypadkowych uszkodzeń danych to królewski ból.
źródło