Jak napisać czytelny kod Clojure?

13

Jestem nowy w Clojure. Rozumiem kod, który piszę, ale później staje się zbyt trudny do zrozumienia.
Trudno dopasować nawiasy.

Jakie są ogólne konwencje dotyczące konwencji nazewnictwa i wcięć w różnych sytuacjach?

Na przykład napisałem przykładowy przykład de-strukturyzacji, aby go zrozumieć, ale za drugim razem wygląda on całkowicie nieczytelny.

(defn f [{x :x y :y z :z [a b c] :coll}] (print x " " y  " " z " " a " " b " " c)) 

Czy w przypadku dekstrukturyzacji lepiej jest to zrobić bezpośrednio na poziomie parametru lub uruchomić formularz let, a następnie kontynuować?

Amogh Talpallikar
źródło
3
Dobra odpowiedź na temat czytelności znajduje się w przepełnieniu stosu. Możesz to sprawdzić. stackoverflow.com/a/1894891/1969106
yfklon
2
Pisanie czytelnego kodu Lisp jest ogólnie trudne. Z jakiegoś powodu wymyślili backronym „Zagubiony w zbędnym nawiasie”.
Mason Wheeler

Odpowiedzi:

23

Konwencje nazewnictwa

  • pozostań małymi literami dla funkcji
  • użyj -do dzielenia wyrazów (podkreślenia lub wielbłąda w innych językach).

    (defn add-one [i] (inc i))

  • Predykaty (tj. Funkcje zwracające wartość prawda lub fałsz) kończą się na ? przykładach:odd? even? nil? empty?

  • Procedury zmiany stanu kończą się za !. Pamiętasz set!prawda? lubswap!

  • Wybierz krótkie nazwy zmiennych w zależności od ich zasięgu. Oznacza to, że jeśli masz naprawdę małą zmienną pomocniczą, często możesz użyć tylko jednej litery. (map (fn [[k v]] (inc v)) {:test 4 :blub 5})w razie potrzeby wybieraj dłuższe nazwy zmiennych, zwłaszcza jeśli są one używane w wielu wierszach kodu i nie można od razu odgadnąć ich celu. (moja opinia).

    Wydaje mi się, że wielu programistów Clojure używa raczej ogólnych i krótkich nazw. Ale to oczywiście nie jest obiektywna obserwacja. Chodzi o to, że wiele funkcji clojure jest w rzeczywistości dość ogólnych.

Funkcje Lambda

  • Możesz nazwać funkcje lambda. Jest to wygodne w przypadku debugowania i profilowania (moje doświadczenie z ClojureScript).

    (fn square-em [[k v]] {k (* v v)})

  • Używaj wbudowanych funkcji lambda #()jako wygodnych

Biała przestrzeń

  • Nie powinny istnieć wiersze zawierające tylko pareny. Od razu zamknij nawiasy. Pamiętaj, że pareny są dostępne dla edytora i kompilatora, wcięcie jest dla ciebie.

  • Listy parametrów funkcji idą w nowym wierszu

   (defn przeciw
     [ab]
     (lista ab))

Ma to sens, jeśli myślisz o ciągach dokumentów. Znajdują się między nazwą funkcji a parametrami. Poniższy ciąg dokumentów prawdopodobnie nie jest najmądrzejszy;)

   (defn przeciw
     „Parowanie rzeczy”
     [ab]
     (lista ab))
  • Sparowane dane można oddzielić nową linią, o ile zachowane zostanie parowanie
  (defn 
    [{x: x 
      y: y 
      z: z  
      [abc]: coll}] 
    (wydrukuj x "" y "" z "" a "" b "" c)) 

(Możesz również wpisać, ,jak chcesz, ale wydaje się to nieciekawe).

  • Do wcięcia użyj wystarczająco dobrego edytora. Wiele lat temu był to emacs do edycji lisp, vim jest także świetny dzisiaj. Typowe środowiska IDE clojure powinny również zapewniać tę funkcjonalność. Po prostu nie używaj losowego edytora tekstu.

    W vimie w trybie poleceń możesz użyć =polecenia, aby poprawnie wciąć.

  • Jeśli polecenie będzie zbyt długie (zagnieżdżone itp.), Możesz wstawić nowy wiersz po pierwszym argumencie. Teraz poniższy kod jest dość bezsensowny, ale ilustruje sposób grupowania i wcięcia wyrażeń:

(+ (if-let [age (: person-age coll))]
     (jeśli (> 18 lat)
       wiek
       0))
   (liczba (zakres (- 3 b))
                 (zmniejsz + 
                         (zakres b 10)))))

Dobre wcięcie oznacza, że ​​nie trzeba liczyć nawiasów. Nawiasy kwadratowe dotyczą komputera (w celu interpretacji kodu źródłowego i jego wcięcia). Wcięcie służy do łatwego zrozumienia.

Funkcje wyższego rzędu vs. fori doseqformy

Pochodząc ze schematu, byłem raczej dumny z tego, że zrozumiałem mapfunkcje lambda itp. Tak często pisałem coś takiego

(map (fn [[k x]] (+ x (k data))) {:a 10 :b 20 :c 30})

To jest dość trudne do odczytania. forForma jest zdecydowanie ładniejszy:

(for [[k x] {:a 10 :b 20 :c30}]
  (+ x (k data)))

`map ma wiele zastosowań i jest naprawdę fajny, jeśli używasz nazwanych funkcji. To znaczy

(map inc [12 30 10]

(map count [[10 20 23] [1 2 3 4 5] (range 5)])

Użyj makr wątków

Użyj makr wątków, ->a ->>także, dotojeśli dotyczy.

Chodzi o to, że makra wątków sprawiają, że kod źródłowy wydaje się bardziej liniowy niż skład funkcji. Poniższy fragment kodu jest dość nieczytelny bez makra wątków:

   (f (g (h 3) 10) [10 3 2 3])

porównać z

   (-> 
     (h 3)
     (g 10)
     (f [10 3 2 3]))

Używając makra wątków, zwykle można uniknąć wprowadzania zmiennych tymczasowych, które są używane tylko raz.

Inne rzeczy

  • Używaj dokumentów
  • krótkie funkcje
  • przeczytaj inny kod clojure
wirrbel
źródło
Ta funkcja z rozstrukturyzowaniem wygląda pięknie z wcięciem!
Amogh Talpallikar
+1 za krótkie funkcje. Wiele małych funkcji jest o wiele bardziej samo dokumentujących
Daniel Gratzer
1
I zdecydowanie zgadzam się, że to dobry pomysł, aby używać nazw zmiennych krótkie, nawet w funkcji „krótkich idące”. Dobre nazwy zmiennych mają kluczowe znaczenie dla czytelności i nie kosztują nic oprócz naciśnięć klawiszy. Jest to jedna z rzeczy, która najbardziej niepokoi mnie w społeczności Clojure. Jest wielu ludzi o niemal wrogim oporze wobec opisowych nazw zmiennych. Rdzeń Clojure jest wypełniony 1-literowymi nazwami zmiennych dla argumentów funkcyjnych, co znacznie utrudnia naukę języka (np. Uruchomioną doclub sourcew REPL). Koniec rant, dla skądinąd doskonałej odpowiedzi
Nathan Wallace
@NathanWallace W pewien sposób zgadzam się z tobą, ale w niektórych aspektach nie. Długie nazwy czasami powodują, że funkcje stają się zbyt specyficzne. Może się więc okazać, że niektóre ogólne operacje filtrowania są w rzeczywistości ogólne, podczas gdy appleszamiast argumentu xswydawało się, że jest specyficzny dla jabłek. Następnie rozważałbym również, że nazwy argumentów funkcji są bardziej sięgające niż powiedzmy zmienną pętli for. więc w razie potrzeby możesz mieć je dłużej. Jako ostatnia myśl: zostawię wam „Nazwa kodu nie wartości” concatenative.org/wiki/view/Concatenative%20language/…
wirrbel
Mogę dodać akapit na temat interfejsów prywatnych i publicznych. Szczególnie jeśli chodzi o biblioteki. Jest to aspekt jakości kodu, o którym nie mówi się wystarczająco dużo i nauczyłem się o tym mnóstwo od czasu napisania tej odpowiedzi.
wirrbel,