2012-11-02 11 views
6

Kiedy mam dane istotne dla funkcji niezależnej od jej argumentów, kiedy powinienem faworyzować enkapsulację bloku nad lokalną enkapsulacją?Enkapsulacja bloku a lokalna enkapsulacja - niech

Kiedy należy używać:

(let [hello "Hello "] 
    (defn do-greet 
    "Print a greeting." 
    [name] 
    (println (str hello name)))) 

Versus:

(defn do-greet 
    "Print a greeting." 
    [name] 
    (let [hello "Hello "] 
    (println (str hello name)))) 

Odpowiedz

6

Pierwsza z nich jest rozsądną opcją, jeśli chcesz użyć wartości stałej statycznej w ramach leksykalnej blokady kodu. Zazwyczaj będzie to zrobić, jeśli:

  • Wartością jest drogie, aby obliczyć i chcesz to zrobić tylko raz, gdy przestrzeń nazw jest ładowany
  • Wartość jest rzeczywiście stały, to znaczy nie zmieni całej wywołania funkcji
  • Wartość zostanie użyta w wielu definicjach funkcji (tj. Wstawisz wiele elementów do bloku dozwolonego)
  • (Możliwe?), Ponieważ chcesz użyć wartości wewnątrz rozszerzenia makr i osadzić let wewnątrz samego rozszerzenia makra spowodowałoby niepotrzebną złożoność.

Ta ostatnia wersja powinna być prawdopodobnie korzystne w większości przypadków, to jest dobre dla kilku powodów:

  • Jest bardziej idiomatyczne
  • Umożliwia określenie funkcji, aby być na najwyższym poziomie, co jest lepsze dla czytelności/zrozumienia kodu
  • Pozwala na zmianę wartości dla różnych wywołań funkcji
  • Lepiej oddaje zamiar użycia wartości w kontekście lokalnym
+0

Podoba mi się to, obejmujesz więcej punktów. Nie jestem pewien, czy punkt makropolecenia jest ważny, ponieważ można zawinąć dookoła ekspansji, ale nadal być wewnątrz funkcji. Dla czytelności sugeruję oddzielenie wyborów stylistycznych od różnic semantycznych. (Wartość może się różnić w zależności od różnych wywołań funkcji). – bmillare

1

Zdecydowanie tak:

(defn do-greet 
    "Print a greeting." 
    [name] 
    (let [hello "Hello "] 
    (println (str hello name)))) 
2

Jeśli hello jest używany tylko w tej jednej funkcji, to ma większy sens wstawić let do samej funkcji. Jeśli zamierzasz używać funkcji hello w wielu funkcjach, warto mieć na zewnątrz let i owijać te funkcje.

+0

Jeśli te dwa elementy nie są równoważne w czasie wykonywania, twierdzę, że nie powinniśmy decydować w oparciu o styl. Zamiast tego wyobraź sobie, że wiązaniem było odczytanie statycznego zasobu '(let [hello (slurp (clojure.java.io/resource" hello "))] ...)'. Zostawiłem moje pytanie bardziej ogólne, ponieważ nie wiem, jak te formularze faktycznie są oceniane i jakie są odpowiednie kompromisy. – ToBeReplaced

+1

W rzeczywistości nie ma to nic wspólnego ze stylem. Jeśli chcesz, aby wartość była dostępna w dwóch różnych funkcjach, musisz udostępnić wartość powyżej zakresu tych funkcji. Zobacz także odpowiedź @ amalloy. – Rayne

5

Jest to wybór stylistyczny i taki, który prawdopodobnie będzie zależał przynajmniej trochę od tego, jak kosztowna jest wartość do obliczenia. Rozważmy zamiast:

(defn nth-prime [n] ...) 

(defn f [x] 
    (let [factor (nth-prime 10000)] 
    (* x factor))) 

(let [factor (nth-prime 10000)] 
    (defn g [x] 
    (* x factor))) 

ponowne obliczanie drogim stałej za każdym razem f nazywa jest marnotrawstwem i g wykorzystuje prostą technikę, aby uniknąć robi.

+3

Mówiąc, że istnieje różnica w działaniu środowiska wykonawczego (wydajność), argumentowałbym, że NIE jest to wybór stylistyczny. Tylko dlatego, że w przykładzie pytającego nie ma praktycznie żadnej różnicy, że jest stylistyczna. – bmillare