2008-11-22 29 views
5

Uczę się programowania funkcjonalnego i próbowałem rozwiązać kilka problemów w funkcjonalnym stylu. Jedną rzeczą, której doświadczyłem, dzieląc swój problem na funkcje, wydawało się, że mam dwie opcje: użyj kilku różnych funkcji z podobnymi listami parametrów lub używając funkcji zagnieżdżonych, które jako zamknięcia mogą po prostu odnosić się do powiązań w funkcji nadrzędnej.Funkcje zagnieżdżone: Niewłaściwe korzystanie z efektów ubocznych?

Chociaż skończyłem na drugim podejściu, ponieważ spowodowało to zmniejszenie liczby wywołań funkcji i wydawało się, że "czuję" się lepiej, z mojego czytania wydaje mi się, że brakuje mi jednego z głównych punktów programowania funkcjonalnego, w tym to wydaje się być "side-effecty"? A teraz te funkcje zagnieżdżone nie mogą modyfikować zewnętrznych powiązań, ponieważ używany przeze mnie język to uniemożliwia, ale jeśli spojrzysz na każdą indywidualną funkcję wewnętrzną, nie możesz powiedzieć "mając te same parametry, ta funkcja zwróci te same wyniki" ponieważ używają zmiennych z zakresu nadrzędnego ... czy mam rację?

Jaki jest pożądany sposób postępowania?

Dzięki!

Odpowiedz

2

Funkcje zagnieżdżania to doskonały sposób podziału pracy na wiele funkcji. To naprawdę nie jest "side-effecty"; jeśli pomaga, pomyśl o przechwyconych zmiennych jako niejawnych parametrach.

Jednym z przykładów, w którym funkcje zagnieżdżone są przydatne, jest zamiana pętli. Parametry funkcji zagnieżdżonej mogą działać jako zmienne indukcyjne, które gromadzą wartości. Prosty przykład:

let factorial n = 
    let rec facHelper p n = 
     if n = 1 then p else facHelper (p*n) (n-1) 
    in 
    facHelper 1 n 

W tym przypadku, nie byłoby naprawdę sensu deklarować funkcję jak facHelper globalnie, ponieważ użytkownicy nie powinni się martwić o parametrze p.

Należy jednak pamiętać, że testowanie funkcji zagnieżdżonych może być trudne, ponieważ nie można ich skierować poza obszar macierzysty.

+0

FacHelper w rzeczywistości nie odnosi się do żadnych wartości zewnętrznych - nadal jest czysto funkcjonalny. –

3

Funkcjonalne programowanie to nie wszystko lub nic. Jeśli zagnieżdżanie funkcji ma więcej sensu, zastosowałem to podejście. Jeśli jednak chcesz, aby funkcje wewnętrzne były całkowicie funkcjonalne, jawnie przekazuj do nich wszystkie potrzebne parametry.

Oto mały przykład na schemacie:

(define (foo a) 
    (define (bar b) 
    (+ a b))  ; getting a from outer scope, not purely functional 
    (bar 3)) 

(define (foo a) 
    (define (bar a b) 
    (+ a b))  ; getting a from function parameters, purely functional 
    (bar a 3)) 


(define (bar a b) ; since this is purely functional, we can remove it from its 
    (+ a b))  ; environment and it still works 

(define (foo a) 
    (bar a 3)) 

Osobiście poszedłbym z pierwszym podejściem, ale albo będzie działać równie dobrze.

1

Rozważmy następujący (contrived) Haskell fragment:

putLines :: [String] -> IO() 
putLines lines = putStr string 
    where string = concat lines 

string jest lokalnie bound nazwany stałą. Ale czy nie jest to także funkcja, która nie przyjmuje argumentów, która zamyka się przed lines, a zatem jest w sposób referencyjny nieprzejrzysta? (W Haskell stałe i funkcje null są rzeczywiście nie do odróżnienia!) Czy uważasz, że powyższy kod jest "nieskuteczny" lub niefunkcjonalny z tego powodu?