2010-02-19 49 views
3

有沒有辦法像(def foo {:two 2 :three (inc (:two this))})那樣模仿this變量?更好的是像(def foo {:two 2 :three (inc ::two)})。有人告訴我有一個圖書館可以做到這一點,但我找不到任何類似的東西。我可以在def中獲得這個變量嗎?

謝謝!

回答

2

更新:重排&返工build-map及(草圖)-m>宏添加。)

你可以寫這個特殊的例子作爲

(def foo (zipmap [:two :three] (iterate inc 2))) 

其發生的最簡單通用的解決方案我在這一刻是

user> (-> {} (assoc :two 2) (#(assoc % :three (inc (:two %))))) 
{:three 3, :two 2} 

我t實際上非常靈活,但它確實需要您重複寫出assoc

爲了實現從問題文本類似的語法,你可以使用這樣的事情:

(defn build-map* [& kvs] 
    (reduce (fn [m [k v]] 
      (assoc m k (v m))) 
      {} 
      kvs)) 

(defmacro build-map [& raw-kvs] 
    (assert (even? (count raw-kvs))) 
    (let [kvs (map (fn [[k v]] [k `(fn [m#] (let [~'this m#] ~v))]) 
       (partition 2 raw-kvs))] 
    `(build-map* [email protected]))) 

user> (build-map :two 2 :three (inc (:two this))) 
{:three 3, :two 2} 

您可以輕鬆地將其更改爲使用用戶提供的符號,而不是硬編碼this。或者你可以切換到%,這只是匿名函數文字之外的常規符號。也許增加一個明確的初始地圖的說法,稱之爲-m>(在地圖線程),你可以在同樣的結果做

(-m> {} :two 2 :three (inc (:two %))) 


另一種時髦的方式(主要是爲了好玩):

;;; from Alex Osborne's debug-repl, 
;;; see http://gist.github.com/252421 
;;; now changed to use &env 
(defmacro local-bindings 
    "Produces a map of the names of local bindings to their values." 
    [] 
    (let [symbols (map key &env)] 
    (zipmap (map (fn [sym] `(quote ~sym)) symbols) symbols))) 

(let [two 2 
     three (inc two)] 
    (into {} (map (fn [[k v]] [(keyword k) v]) (local-bindings)))) 
{:two 2, :three 3} 

注意,這也將捕獲任何外令的形式出臺了綁定......

4

如果你想有一個臨時的名稱的東西,這就是let是。

(def foo (let [x {:two 2}] 
      (assoc x :three (inc (:two x))))) 

我不知道任何你想做的圖書館。每隔一段時間,有人會建議一個「廣義箭頭」,如->,但帶有一個魔法符號,您可以將其粘在中間表達式中,並由其他內容代替。例如參見herehere。但是這個想法往往會被打倒,因爲它比較複雜並且沒有什麼好處。 let是你的朋友。請參閱Rich的示例:

(let [x [] 
     x (conj x 1) 
     x (into x [2 3]) 
     x (map inc x)] 
...) 
+1

+1針對新複雜魔法構造的現有解決方案。 – Leonel 2010-02-22 16:06:07

相關問題