(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:
- I modified your first branch to a simpler (imho) impl using zipmap and an infinite sequence.
- Instead of (count (keys map)), just doing (count map) is sufficient (the count here is inside your helper function).
精彩评论