开发者

Is it possible to set a common binding for all implementations in a multi-signature function?

(This is a question regarding style. I am aware this can be done with a bunch of conditionals, multimethods, etc.)

In the following function, null-vector is defined on each implementation. How can I set it once for the entire function? In general, is it possible to set a common binding to all implementations?

A closure won't work, sin开发者_C百科ce it null-vector needs an "argument", but I suppose I could partial it. However, that would still need computation of the size parameter. I'd like to avoid repeating code, obviously.

(defn path
  "Returns a lazy sequence of vectors representing a monotonic path
   walked over coll in n-dimensional space, where n is the cardinality
   of coll's alphabet."

  ([coll]
     (let [alphabet    (set coll)
           cardinality (count alphabet)
           alpha-map   (apply hash-map (interleave alphabet (range cardinality)))
           null-vector (vec (repeat cardinality 0))]
       (path coll null-vector alpha-map)))

  ([coll alpha-map]
     (let [null-vector (vec (repeat (count (keys alpha-map)) 0))]
       (path coll null-vector alpha-map)))

  ([coll origin alpha-map]
     (let [null-vector (vec (repeat (count origin) 0))
           unit-vector #(assoc null-vector (alpha-map %) 1)
           sum-vectors #(vec (map + %1 %2))]
       (reductions sum-vectors origin (map unit-vector coll)))))


I would create a "private" helper function:

(defn- null-copy-vector [coll]
  (vec (repeat (count coll) 0)))

and then just call it in each branch of the function:

(defn path
  "Returns a lazy sequence of vectors representing a monotonic path
   walked over coll in n-dimensional space, where n is the cardinality
   of coll's alphabet."

  ([coll]
     (let [alphabet    (set coll)
           alpha-map   (zipmap alphabet (iterate inc 0))  ;; note 1
           null-vector (null-copy-vector alphabet)]
       (path coll null-vector alpha-map null-vector)))

  ([coll alpha-map]
     (let [null-vector (null-copy-vector alpha-map)]      ;; note 2
        (path coll null-vector alpha-map null-vector))) 

  ([coll origin alpha-map]
     (path coll origin alpha-map (null-copy-vector origin)))

  ([coll origin alpha-map null-vector]
     (let [unit-vector #(assoc null-vector (alpha-map %) 1)
           sum-vectors #(vec (map + %1 %2))]
       (reductions sum-vectors origin (map unit-vector coll)))))

It may be this isn't satisfying to you because null-copy-vector isn't "inside" the overall function here, but I think this is pretty idiomatic. On a function that did not take multiple arities, I might use letfn to separate out an "internal" function but that won't work here.

Breaking things up like this also lets you a) reuse the basic building block functions elsewhere and b) lets you test in smaller chunks. You might want to skip the defn- and just use defn to make testing easier (although it is possible to test defn- with a bit more work).

I also broke out a new 4-arg form that takes the null-vector as the last arg, letting you pass it in directly if you know it so that you can avoid re-creating it from an already null vector. If you wanted to hide that 4-arg form, you could pull it into a separate defn- helper function.

Unrelated notes:

  1. I modified your first branch to a simpler (imho) impl using zipmap and an infinite sequence.
  2. Instead of (count (keys map)), just doing (count map) is sufficient (the count here is inside your helper function).
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜