How to parse nested parameters in a Clojure defn?

I was browsing some Clojure source code and came across this function:

(defn draw-mask [app-state [r c]]
  (let [x1 (+ 25 (* c 50))
        y1 (+ 25 (* r 50))]
    [:circle {:cx x1 :cy y1 :r 12 :fill "white"}]))

What I do not understand is how [app-state [rc]] is parsed. What would a typical data structure passed to this and how does defn parcel it out. Any references to this in the clojure docs would be appreciated, especially since ClojureDocs.org was of no help on the subject.


The function draw-mask in your example takes two arguments. The first is app-state , which is not used in your code, and the second can be one of several different types of data: values from a map, a string, a list, or most commonly, a vector. You can see the different types here that can be used in nthFrom in the clojure code.

This is called sequential destructuring, and as other comments above mention, it's a big topic. However, for your case, this is how it works:

(draw-mask xyz [3 9])     ---> In draw-mask, r is 3, and c is 9.
(draw-mask xyz [3])       ---> In draw-mask, r is 3, and c is nil.
(draw-mask xyz [3 9 12])  ---> In draw-mask, r is 3, and c is 9 -- 12 is not bound

One clarification here: whether or not a structure is seqable? is not the main criteria for being able to be destructured. For example, a set is seqable? but cannot be destructured. The main criteria for sequential destructuring (different from associative destructuring, using maps, which is not discussed here) is that nth must be supported on it. In RT.java, you will see the list of possible types. They are: CharSequence , a native Java array, RandomAccess , Matcher , Map.Entry , and Sequential ( Sequential will cover the most common structures: list and vector).


Correction

As @Josh points out, sequential destructuring requires nth to work: seqability is not enough.


This is a simple case of sequential destructuring. Let's use a function that shows what's going on:

(defn foo [app-state [r c]]
 {:app-state app-state, :r r, :c c})
  • The first argument, app-state , can be anything.
  • The second argument, [rc] , must be something like a sequence that nth applies to. Then
  • r is its first element, and
  • c is its second.
  • If the sequence isn't long enough, these yield nil .
  • Examples:

    (foo 1 ())
    ;{:app-state 1, :r nil, :c nil}
    
    (foo inc "hello, world!")
    ;{:app-state #<core$inc clojure.core$inc@32d23c2f>, :r h, :c e}
    
    (foo :one [:two :three :four])
    ;{:app-state :one, :r :two, :c :three}
    
    (foo  "Flubalub" (drop 5 (iterate #(* 10 %) 1)))
    ;{:app-state "Flubalub", :r 100000, :c 1000000}
    

    But

    (foo 99 #{1 2})
    ;java.lang.UnsupportedOperationException: nth not supported on this type: PersistentHashSet
    ; ...
    
    链接地址: http://www.djcxy.com/p/82580.html

    上一篇: Clojure给反射警告depsite类型提示

    下一篇: 如何解析Clojure定义中的嵌套参数?