Problema ao passar um vetor como uma ligação para a macro

9

Eu tenho um número arbitrário de listas que gostaria de processar usando a macro for. Eu quero criar uma função que passa um vetor como ligação, já que o número de listas varia.

Se codifico a ligação, funciona como esperado:

=> (def list1 '("pink" "green"))
=> (def list2 '("dog" "cat"))
=> (for [A list1 B list2] (str A "-" B))
("pink-dog" "pink-cat" "green-dog" "green-cat")

Quando tento criar um vetor separadamente e usá-lo como ligação, ocupo problemas. Aqui eu manualmente criar o vetor de ligações:

=> (def testvector (vec (list 'A list1 'B list2)))

isso parece bem:

=> testvector
[A ("pink" "green") B ("dog" "cat")]
=> (class testvector)
clojure.lang.PersistentVector

No entanto,

=> (for testvector (str A "-" B))
#<CompilerException java.lang.IllegalArgumentException: for requires a vector for its binding (NO_SOURCE_FILE:36)>

Não entendo porque o testvector não é considerado um vetor quando usado como ligação. Agarrando palhas, coloquei testvector entre colchetes, que mantém a macro feliz (vê um vetor), mas agora eu tenho um vetor com um elemento (ou seja, um vetor dentro de um vetor) e isso não funciona porque a ligação precisa seja pares de nome e coleção.

=> (for [testvector] (str A "-" B))
#<CompilerException java.lang.IllegalArgumentException: for requires an even number of forms in binding vector (NO_SOURCE_FILE:37)>

Qualquer sugestão sobre como passar dinamicamente um vetor como uma ligação para seria apreciada.

    
por Teflon Mac 15.07.2010 в 02:49
fonte

4 respostas

5

A chave é que para é uma macro. No momento da macroexpansão, o testvector é um símbolo. Ele será avaliado para um vetor no momento da avaliação, mas não é um vetor na perspectiva da macro para .

user=> (defmacro tst [v] (vector? v))
#'user/tst
user=> (tst testvector)
false
user=> (vector? testvector)
true
user=> (defmacro tst2 [v] '(vector? ~v))
#'user/tst2
user=> (tst2 testvector)
true

Se você verificar a fonte da macro para (em core.clj), verá que para usa um vetor não-destacado? chamar, assim como tst no exemplo acima.

    
por G__ 15.07.2010 / 02:57
fonte
0

Aqui está um método de último recurso. Esteja avisado, onde quer que você veja read-string que é código para Here Be Dragons! (Devido a riscos de segurança e falta de garantias de consistência em tempo de compilação sobre o comportamento do seu código)

(def list1 '("pink" "green"))
(def list2 '("dog" "cat"))
(for [A list1 B list2] (str A "-" B))

(def testvector (vec (list 'A list1 'B list2)))

(def testvector-vec (vec (list 'A (vec list1) 'B (vec list2))))

(def for-string (str "(for " testvector-vec "(str A \"-\" B))"))

(eval (read-string for-string))
> ("pink-dog" "pink-cat" "green-dog" "green-cat")
    
por hawkeye 07.02.2015 / 13:07
fonte
0

Embora não seja uma solução para o seu problema, deve-se notar que o que você está fazendo pode ser mais facilmente alcançado com o mapa em vez de, por exemplo,

user=> (def list1 '("pink" "green"))
#'user/list1
user=> (def list2 '("dog" "cat"))
#'user/list2
user=> (map #(str %1 "-" %2) list1 list2)
("pink-dog" "green-cat")
user=> 

Outra técnica útil ao aprender e experimentar é usar palavras-chave em vez de strings. Isso pode reduzir a digitação, ou seja, não há necessidade de colocar os valores entre aspas e, às vezes, pode ajudar a identificar erros com mais facilidade. Em vez de (def list1 '("pink" "green")) você pode fazer (def list1' (: pink: green)). Melhor ainda, em vez de usar listas, tente usar vetores e, em seguida, não é necessário citar (salvar outro pressionamento de tecla).

    
por Tim X 08.02.2015 / 00:31
fonte
0

Você pode tentar forçar a avaliação do vetor de ligação. Em vez de tentar definir uma macro que envolverá a macro for , envolva-a em uma função, por exemplo,

(defn for-fn [bindings expr]
  (eval '(for ~bindings ~expr))) 

Então você pode realmente construir um vetor de ligação com algumas restrições adicionais, já que todas as expressões-s dentro do vetor de ligação precisam ser válidas e conter um verbo como o primeiro elemento.

(let [bindings '[a (list 1 2) b (list 3 4) c (range 10 12)
                 :when (> (+ a b c) 15)]
      expr '(str a "-" b "-" c)]
  (for-fn bindings expr)) 

E com o seu exemplo:

(def list1 '("pink" "green"))
(def list2 '("dog" "cat"))
(def testvector (vector 'A (cons 'list  list1) 'B (cons 'list list2)))

(for-fn testvector '(str A "-" B))
=> ("pink-dog" "pink-cat" "green-dog" "green-cat")

Nota: como for-fn é função, você precisa citar a expressão (str A "-" B) para evitar uma avaliação antecipada (antes de A e B estarem vinculados).

    
por T.Gounelle 09.02.2015 / 16:07
fonte