2010-12-12 111 views
4

J'ai remarqué ma propre habitude d'utiliser des vecteurs beaucoup plus souvent que des listes quand j'ai besoin d'un littéral pour tester une fonction qui prend une séquence.Listes pour les expressions S, Vecteurs pour les données (littérales)?

I.e. :

(map inc [1 2 3])

Mais pas:

(map inc (list 1 2 3))

Bien qu'ils soient à la fois correct (et les quelques caractères supplémentaires dans le second peuvent être résolus avec les guillemets). Donc, je n'utilise pratiquement les listes que pour construire des expressions s, que je le fasse manuellement ou dans une macro. En ne tenant pas compte des problèmes de performance, dont on a déjà parlé here, que considéreriez-vous comme un meilleur style de codage?

Je sais que c'est une question un peu subjective, mais il m'est apparu que peut-être je reprends mon état d'esprit de «séparation de code et de données» de mon expérience dans les langages non-lisp.

Répondre

3

un vecteur dans Clojure signifie vaguement "ce sont des données". Le groupe de liaisons pour un let ou fn est juste cela, des données. C'est une séquence de symboles, ou une séquence de paires de symboles-valeurs.

Une liste signifie vaguement "Ceci est un appel de fonction ou de macro". Quand vous voyez une liste, vous serez assez sûr en supposant que la première chose dans cette liste est quelque chose d'appelable, et le reste des choses dans la liste sont des arguments.

Il existe des exceptions. Vous verrez à la fois (ns foo (:use (bar baz))) et (ns foo [:use [bar baz]]) dans le code idiomatique. Je ne sais pas pourquoi autre que Clojure a grandi très rapidement et semi-organiquement dans certaines régions et continue de croître. Mais c'est une bonne règle.

Les vecteurs n'ont pas besoin d'être quotés, ce qui est un bon moyen d'éviter certains bugs, d'autant plus que tant de choses dans Clojure sont appelables en tant que fonctions. (:foo :bar) n'est pas une liste de deux mots-clés, mais il va compiler, appelant :foo comme une recherche par mot-clé sur le mot-clé :bar, évaluant comme nil. Il n'y a aucun moyen de bousiller [:foo :bar]. C'est toujours un vecteur de deux mots-clés. L'avantage de ne pas avoir à citer les éléments d'un vecteur ne doit pas être sous-estimé. Comment aimeriez-vous écrire cela avec des listes?

(let [x 123 y 456] 
    [[:foo x] [:bar y]]) 

Une façon est si bavard que vos données sont perdues dans une forêt de list. L'autre façon est un désordre inutile de la ponctuation.

(let [x 123 y 456] 
    (list (list :foo x) (list :bar y))) 

(let [x 123 y 456] 
    `((:foo ~x) (:bar ~y))) 

Clojure est peut-être moins d'une soupe de parenthèse que d'autres dialectes Lisp, grâce à [] et {}. Bien que certaines personnes détestent le ))])}) qui Clojure finit par avoir, je pense qu'il est préférable de vous perdre dans une longue série de )))))))).

« Code sont des données » ne signifie pas que toutes vos données doit ressembler à tous de votre code et vice versa. Vous avez accès au code-en-données lorsque vous en avez besoin, ce qui est rarement, généralement dans les macros. En attendant, si vos données ne sont pas du code, pourquoi devrait-il ressembler à du code? Une séquence de numéros de téléphone n'est pas un code. Il n'y a aucune raison pour que cela figure dans une liste.

Et comme vous l'avez dit, il y a des problèmes de performance avec des listes.

+0

Alors, mes craintes étaient exagérées. C'est idiomatique après tout. Merci! –

4

Les littéraux vectoriels, lorsque les performances ne posent aucun problème, sont généralement préférés aux listes; Premièrement, ils sont plus faciles à taper, et moins de caractères, mais (probablement) plus important: ils sont plus faciles à lire. (plus facile de se différencier des appels de fonction, etc.)

En fait, je suis sûr que la facilité de lisibilité des littéraux vectoriels est la raison de leur mise en application dans de nombreuses macros.

(let (a 1) a) ;; => Exception: let requires a vector for it's binding

(let [a 1] a) ;; => 1