2010-03-21 7 views

Odpowiedz

30

funkcja Clojure za "stażysta" jest w tym celu:

(doseq [x ["a" "b" "c"]] 
    (intern *ns* (symbol x) 666)) 
+0

Najbardziej zwięzła i bezpośrednia (możliwa?) Odpowiedź. Dałem ci znak i nadzieję, że sepp2k nie jest szalony, że go stracił. Dzięki! –

13
(doall (for [x ["a" "b" "c"]] (eval `(def ~(symbol x) 666)))) 

W odpowiedzi na Twój komentarz:

Brak zaangażowane tutaj makr. eval to funkcja, która pobiera listę i zwraca wynik wykonania tej listy jako kod. `i ~ są skrótami, aby utworzyć listę częściowo cytowaną.

`oznacza zawartość następujących wykazów być cytowane, chyba że poprzedzony ~

~ poniższej liście jest wywołanie funkcji, które powinny być wykonane, nie notowane.

Więc `` (def ~ (symbol x) 666) is the list containing the symbol def , followed by the result of executing symbol x followed by the number of the beast. I could as well have written (eval (lista „def (symbol x) 666))`, aby osiągnąć ten sam efekt.

+0

Znałem tę odpowiedź zaangażowany eval ale jakoś zapomniałem o ~ konstruktu. –

+0

Cóż, wiem, po prostu działa! Nie spodziewałem się, że konieczne będzie przejście do poziomu makro, aby to osiągnąć, ale z przyjemnością dołączę to do mojej "książki kucharskiej na rzeczy, których nie rozumiem w pełni". Dziękuję Ci! –

+2

@CarlSmotricz: Właśnie dodałem kilka objaśnień do kodu. Mam nadzieję, że teraz lepiej rozumiesz. – sepp2k

6

Zaktualizowano komentarz Stuarta Sierra (z podaniem clojure.core/intern).

Stosowanie tutaj jest w porządku, ale może być interesujące wiedzieć, że nie jest to konieczne, niezależnie od tego, czy Vars są już znane. W rzeczywistości, jeśli są znane, to myślę, że poniższe rozwiązanie jest czystsze; gdyby nie istniały, to nie nalegałbym, by moja alternatywna propozycja była znacznie czystsza, ale wydaje się, że jest to najkrótszy kod (jeśli pominiemy narzut z trzech linii dla definicji funkcji), więc po prostu opublikuję to dla twojego rozważenia.


Jeśli Var jest znany istnieją:

(alter-var-root (resolve (symbol "foo")) (constantly new-value)) 

Więc można zrobić

(dorun 
    (map #(-> %1 symbol resolve (alter-var-root %2)) 
     ["x" "y" "z"] 
     [value-for-x value-for-y value-for z])) 

(Jeśli ta sama wartość miał być stosowany dla wszystkich Vars, można użyć (repeat value) dla końcowego argumentu do mapowania lub po prostu umieść go w anonimowej funkcji.)


Jeśli Vars może muszą być tworzone, a następnie rzeczywiście można napisać funkcję, aby to zrobić (po raz kolejny, nie koniecznie twierdzą, że jest to czystsze niż eval, ale w każdym razie - tylko dla odsetki od niego):

(defn create-var 
    ;; I used clojure.lang.Var/intern in the original answer, 
    ;; but as Stuart Sierra has pointed out in a comment, 
    ;; a Clojure built-in is available to accomplish the same 
    ;; thing 
    ([sym] (intern *ns* sym)) 
    ([sym val] (intern *ns* sym val))) 

Zauważ, że jeśli Var okazuje się już internowany o podanej nazwie w danej przestrzeni nazw, to nic nie zmienia w przypadku jeden argument lub po prostu resetuje Var do podana nowa wartość w przypadku dwóch argumentów. Dzięki temu można rozwiązać pierwotnego problemu tak:

(dorun (map #(create-var (symbol %) 666) ["x" "y" "z"])) 

kilka dodatkowych przykładów:

user> (create-var 'bar (fn [_] :bar)) 
#'user/bar 
user> (bar :foo) 
:bar 

user> (create-var 'baz) 
#'user/baz 
user> baz 
; Evaluation aborted. ; java.lang.IllegalStateException: 
         ; Var user/baz is unbound. 
         ; It does exist, though! 

;; if you really wanted to do things like this, you'd 
;; actually use the clojure.contrib.with-ns/with-ns macro 
user> (binding [*ns* (the-ns 'quux)] 
     (create-var 'foobar 5)) 
#'quux/foobar 
user> quux/foobar 
5 
+0

Bardzo fajne i bardzo pouczające, wielkie dzięki! Na pierwszy rzut oka wygląda to na działanie kodu 'def'; Domyślam się, że to "rozwidlenie" def. Mogę użyć tego na moim następnym mini-projekcie. –

+1

Zasada: nie używaj eval, dopóki nie będzie to konieczne i wiesz, co robisz. Poszedłbym ze stażystą, by rozwiązać problem; ta odpowiedź pokazała ładnie, że eval nie jest tutaj potrzebny. – danlei

+2

Var.intern jest dostępny jako wbudowana funkcja Clojure "intern" –

3

zasady oceny dla normalnych wywołań funkcji mają ocenić wszystkie elementy z listy, a następnie zadzwonić pierwszy pozycja na liście jako funkcja z pozostałymi pozycjami na liście jako parametrami.

Nie można jednak przyjąć żadnych założeń dotyczących reguł oceny dla specjalnych formularzy lub makr. Specjalna forma lub kod wygenerowany przez wywołanie makra może oceniać wszystkie argumenty lub nigdy ich nie oceniać lub oceniać wielokrotnie, lub oceniać niektóre argumenty, a nie inne. def jest specjalną formą i nie ocenia swojego pierwszego argumentu. Jeśli tak, to nie może działać. Ocena foo w (def foo 123) spowodowałaby błąd "no such var 'foo" "przez większość czasu (jeśli foo został już zdefiniowany, prawdopodobnie nie zdefiniowałbyś go samodzielnie).

Nie jestem pewien, z czego korzystasz, ale nie wydaje się to bardzo idiomatyczne. Korzystanie z usługi w dowolnym miejscu, ale na najwyższym poziomie programu zazwyczaj oznacza, że ​​robisz coś złego.

. (Uwaga: doall + for = doseq)

+0

Dzięki za przypomnienie o 'doseq'! Definiuję garść zmiennych na najwyższym poziomie i po prostu chciałem ograniczyć pisanie. –

+2

Dlaczego zamiast symboli nie należy używać symboli? –

+0

Ponieważ nazwy, które planowałem użyć, zostały obliczone (przez konkatenację, natch).Podkreślam, że robię to tylko dla celów eksperymentalnego, "szybkiego i brudnego" kodu; istnieje wiele lepszych sposobów na wyrażenie tego, co robię. Wysłałem to pytanie tylko dlatego, że chciałem to zrobić w moim wczesnym szkicu kodu i byłem zirytowany tym, że znakomity język Clojure wydawał się nie wspierać tego szczególnego problemu jiggerów. –

Powiązane problemy