2011-10-17 30 views
5

我定義了一個函數,它帶有兩個參數 - map和一個鍵。關鍵是從地圖參數分解defn vs讓我們分解

(defn myfunc [{v k} k] 
    v) 

引用當我打電話:

(myfunc {:a 10} :a) 

令人驚訝地產生預期的結果:10

類似的事情在咱們:

(let [{v k} {:a 10} k :a] v) 

失敗,因爲k未定義在當第一部分被評估時。

我的問題是:爲什麼函數參數分解的行爲與的分解行爲不同,讓表達式?

回答

3

Macroexpanding的defn形式我得到的這個等價(除去.withMeta的東西,並重新格式化):

(def myfunc 
    (fn* myfunc 
     ([p__11393 k] 
      (let* [map__11394 p__11393 
       map__11394 (if (seq? map__11394) 
           (apply hash-map map__11394) 
           map__11394) 
       v   (get map__11394 k)] 
       v)))) 

所以在這裏我們可以看到,{v k}地圖其實首先分配給一個局部變量p__11393。然後,if語句測試該變量是否實際上是一個序列,如果是,則將其轉換爲哈希映射,否則保持原樣。重要的是,分配給k的值在之後在地圖中查找,因此這種工作沒有錯誤(並且如果:a不在地圖中,get在那種情況下返回nil也會如此)。

在另一方面macroexpanding的let形式,我得到

(let* 
    [map__11431 
    {:a 10} 
    map__11431 
    (if (seq? map__11431) (apply hash-map map__11431) map__11431) 
    v 
    (get map__11431 k) 
    k 
    :a] 
    v) 

,在這裏我們可以看到,v得到的(get map__11431 k)的結果,但k沒有在這一點上還沒有定義,因此錯誤。

+0

現在的問題是,如果這樣的差異已經被認爲是一個錯誤或功能... – aav

+0

我要說如果有人知道解構發生在函數體內,這並不奇怪,因此不是一個錯誤。但我認爲這個事實應該在適當的地方解釋得更好 - 例如,如果我沒有記錯的話,Clojure的喜悅就沒有真正理解這一點。 – Paul

+0

什麼是有趣的(但並不令人驚訝),他不會工作:(defn myfunc [{v k} {k:k1}] v) – aav

0

這不是一個完整的答案,但下面不工作:

user=> (defn myfunc [{v k} k] v) 
#'user/myfunc 
user=> (myfunc {:a 10} :a) 
10 
user=> (let [k :a {v k} {:a 10}] v) 
10 
user=> 
+0

這當然會起作用,因爲* k * – aav