I'm currently working on a pdf generating library built around pdfbox, a java library.
I don't have a problem per se, I'm just uncertain of what would be the clever way in clojure to do something.
I try to stick to a Hiccup style syntax for generating pdf.
With something like that (a very impractical example):
[:page {:title "hey"}
[:frame {:name "frame1" :top 130}]]
I would like to retrieve later in the document the values passed to page and frame (which are functions after parsing). For example, the next frame:
[:frame {:bottom (+ 10 (:top "frame1"))} (str "Titre:" (:title page))]
Every function passes its options map to the other so the first frame's options actually look like this:
{:title "hey", :name "frame1", :top 130}
But obviously the user can't access that map when the executing this kind of code.
For the page I think using a global Var that is updated with binding seems to be an okay solution (open to any suggestions). But as there might be any number of frames they can't be declared earlier. Therefore, my question is:
What kind of function, concept or way of doing things would be best to deal with that kind of problem? How could I give the user the ability to retrieve these data? (avoiding a global var for all options and a get-in if possible)
i've got an idea about that: why don't you use dynamically scoped value for context, that would contain all the data for your struct's call stack. And then you can analyze your struct, evaluating in this context.
I would go with something like this:
(def ^:dynamic *context* ())
(defn lookup-context [& kv-pairs]
(some #(when (every? (fn [[k v]] (= (k %) v)) kv-pairs) %)
*context*))
(defmacro with-context [data]
(let [items (tree-seq #(and (vector? %) (#{:frame :page} (first %)))
#(nthnext % 2)
data)
ctx-items (reverse (map second items))
let-bindings (zipmap ctx-items (repeatedly gensym))
data (clojure.walk/postwalk-replace let-bindings data)]
(reduce (fn [acc [itm sym]]
`(let [~sym ~itm]
(binding [*context* (cons ~sym *context*)] ~acc)))
data ;; here goes your data parsing
let-bindings)))
so this macro establishes cascading dynamic bindings, and all the calls to lookup-context inside it (even in the nested functions called from ";;here goes your data parsing" part)
for example with this structure:
(with-context [:page
{:name "page0" :val 1000}
[:frame
{:name "frame0" :val 10}
[:frame {:name "frame1" :val (+ (:val (lookup-context [:name "page0"]))
(:val (lookup-context [:name "frame0"])))}]]])
it is going to be expanded to this:
(let [G__8644 {:name "page0", :val 1000}]
(binding [*context* (cons G__8644 *context*)]
(let [G__8643 {:name "frame0", :val 10}]
(binding [*context* (cons G__8643 *context*)]
(let [G__8642 {:name "frame1",
:val
(+
(:val (lookup-context [:name "page0"]))
(:val (lookup-context [:name "frame0"])))}]
(binding [*context* (cons G__8642 *context*)]
[:page G__8644 [:frame G__8643 [:frame G__8642]]]))))))
giving you the result you need, i guess
UPDATE
as an answer to #amalloy's question about the reason for dynamically scoped var usage:
user> (defn item-factory []
[:frame {:name "frame2" :val (+ (:val (lookup-context [:name "frame1"]))
(:val (lookup-context [:name "page0"])))}])
#'user/item-factory
user>
(with-context [:page
{:name "page0" :val 1000}
[:frame
{:name "frame0" :val 10}
[:frame {:name "frame1" :val (+ (:val (lookup-context [:name "page0"]))
(:val (lookup-context [:name "frame0"])))}]
(item-factory)]])
;;=> [:page {:name "page0", :val 1000}
;; [:frame {:name "frame0", :val 10}
;; [:frame {:name "frame1", :val 1010}]
;; [:frame {:name "frame2", :val 2010}]]]
as you can see, the item-factory function, being called inside the data processing, is also context aware, meaning that the lib user can simply decompose the data generation, keeping the implicit dependency on the items defined upper on the definitions stack.
Related
Let's say I have several vectors
(def coll-a [{:name "foo"} ...])
(def coll-b [{:name "foo"} ...])
(def coll-c [{:name "foo"} ...])
and that I would like to see if the names of the first elements are equal.
I could
(= (:name (first coll-a)) (:name (first coll-b)) (:name (first coll-c)))
but this quickly gets tiring and overly verbose as more functions are composed. (Maybe I want to compare the last letter of the first element's name?)
To directly express the essence of the computation it seems intuitive to
(apply = (map (comp :name first) [coll-a coll-b coll-c]))
but it leaves me wondering if there's a higher level abstraction for this sort of thing.
I often find myself comparing / otherwise operating on things which are to be computed via a single composition applied to multiple elements, but the map syntax looks a little off to me.
If I were to home brew some sort of operator, I would want syntax like
(-op- (= :name first) coll-a coll-b coll-c)
because the majority of the computation is expressed in (= :name first).
I'd like an abstraction to apply to both the operator & the functions applied to each argument. That is, it should be just as easy to sum as compare.
(def coll-a [{:name "foo" :age 43}])
(def coll-b [{:name "foo" :age 35}])
(def coll-c [{:name "foo" :age 28}])
(-op- (+ :age first) coll-a coll-b coll-c)
; => 106
(-op- (= :name first) coll-a coll-b coll-c)
; => true
Something like
(defmacro -op-
[[op & to-comp] & args]
(let [args' (map (fn [a] `((comp ~#to-comp) ~a)) args)]
`(~op ~#args')))
Is there an idiomatic way to do this in clojure, some standard library function I could be using?
Is there a name for this type of expression?
For your addition example, I often use transduce:
(transduce
(map (comp :age first))
+
[coll-a coll-b coll-c])
Your equality use case is trickier, but you could create a custom reducing function to maintain a similar pattern. Here's one such function:
(defn all? [f]
(let [prev (volatile! ::no-value)]
(fn
([] true)
([result] result)
([result item]
(if (or (= ::no-value #prev)
(f #prev item))
(do
(vreset! prev item)
true)
(reduced false))))))
Then use it as
(transduce
(map (comp :name first))
(all? =)
[coll-a coll-b coll-c])
The semantics are fairly similar to your -op- macro, while being both more idiomatic Clojure and more extensible. Other Clojure developers will immediately understand your usage of transduce. They may have to investigate the custom reducing function, but such functions are common enough in Clojure that readers can see how it fits an existing pattern. Also, it should be fairly transparent how to create new reducing functions for use cases where a simple map-and-apply wouldn't work. The transducing function can also be composed with other transformations such as filter and mapcat, for cases when you have a more complex initial data structure.
You may be looking for the every? function, but I would enhance clarity by breaking it down and naming the sub-elements:
(let [colls [coll-a coll-b coll-c]
first-name (fn [coll] (:name (first coll)))
names (map first-name colls)
tgt-name (first-name coll-a)
all-names-equal (every? #(= tgt-name %) names)]
all-names-equal => true
I would avoid the DSL, as there is no need and it makes it much harder for others to read (since they don't know the DSL). Keep it simple:
(let [colls [coll-a coll-b coll-c]
vals (map #(:age (first %)) colls)
result (apply + vals)]
result => 106
I don't think you need a macro, you just need to parameterize your op function and compare functions. To me, you are pretty close with your (apply = (map (comp :name first) [coll-a coll-b coll-c])) version.
Here is one way you could make it more generic:
(defn compare-in [op to-compare & args]
(apply op (map #(get-in % to-compare) args)))
(compare-in + [0 :age] coll-a coll-b coll-c)
(compare-in = [0 :name] coll-a coll-b coll-c)
;; compares last element of "foo"
(compare-in = [0 :name 2] coll-a coll-b coll-c)
I actually did not know you can use get on strings, but in the third case you can see we compare the last element of each foo.
This approach doesn't allow the to-compare arguments to be arbitrary functions, but it seems like your use case mainly deals with digging out what elements you want to compare, and then applying an arbitrary function to those values.
I'm not sure this approach is better than the transducer version supplied above (certainly not as efficient), but I think it provides a simpler alternative when that efficiency is not needed.
I would split this process into three stages:
transform items in collections into the data in collections you want to operate
on - (map :name coll);
Operate on transformed items in collections, returning collection of results - (map = transf-coll-a transf-coll-b transf-coll-c)
Finally, selecting which result in resulting collection to return - (first calculated-coll)
When playing with collections, I try to put more than one item into collection:
(def coll-a [{:name "foo" :age 43} {:name "bar" :age 45}])
(def coll-b [{:name "foo" :age 35} {:name "bar" :age 37}])
(def coll-c [{:name "foo" :age 28} {:name "bra" :age 30}])
For example, matching items by second char in :name and returning result for items in second place:
(let
[colls [coll-a coll-b coll-c]
transf-fn (comp #(nth % 1) :name)
op =
fetch second]
(fetch (apply map op (map #(map transf-fn %) colls))))
;; => false
In transducers world you can use sequence function which also works on multiple collections:
(let
[colls [coll-a coll-b coll-c]
transf-fn (comp (map :name) (map #(nth % 1)))
op =
fetch second]
(fetch (apply sequence (map op) (map #(sequence transf-fn %) colls))))
Calculate sum of ages (for all items at the same level):
(let
[colls [coll-a coll-b coll-c]
transf-fn (comp (map :age))
op +
fetch identity]
(fetch (apply sequence (map op) (map #(sequence transf-fn %) colls))))
;; => (106 112)
Not sure what is going on here, but I have this code, where the map function successfully executes in my repl without being wrapped in a defined function:
(def dogs '({:name "scout" :age 5} {:name "rux" :age 3} {:name "fenley" :age 2}))
(def ages (atom {:above-four '() :below-four '()}))
(map
#(if (> (get-in % [:age]) 4)
(swap! ages update-in [:above-four] merge %)
(swap! ages update-in [:below-four] merge %)) dogs)
#ages
=> {:above-four ({:name "scout", :age 5}), :below-four ({:name "fenley", :age 2} {:name "rux", :age 3})}
Yet, when I define the map function as such:
(def ages (atom {:above-four '() :below-four '()}))
(def dogs '({:name "scout" :age 5} {:name "rux" :age 3} {:name "fenley" :age 2}))
(defn test-dogs []
(map
#(if (> (get-in % [:age]) 4)
(swap! ages update-in [:above-four] merge %)
(swap! ages update-in [:below-four] merge %)) dogs)
#ages)
I get the following result:
=> {:above-four (), :below-four ()}
I'm very confused, because this function taken straight from the Clojure docs works just fine:
(def m1 (atom {:a "A" :b "B"}))
(defn update-m1 []
(swap! m1 assoc :a "Aaay")
#m1)
=> {:a "Aaay", :b "B"}
Because test-dogs uses map, it returns a lazy sequence. The elements of lazy sequences aren't realized until they're needed.
The problem with your set up is you're trying to use map to run a side effect (the call to swap!; an impure action), and never actually use the result of map. Because you never request results from map, the mapping function containing swap! never runs.
By using mapv (which returns a non-lazy vector), or doseq (which is meant to carry out side effects):
(doseq [dog dogs]
(let [k (if (> (:age dog) 4)
:above-four
:below-four)]
(swap! ages update k merge dog)))
You can force the side effects to run.
I cleaned up the code a bit. The -in versions you were using were unnecessary; as was the the call to get-in. I also got rid of the redundant calls to swap!.
Note though that at least in your example, use of atoms is entirely unnecessary. Even if you have a more complicated use case, make sure their use is justified. Mutable variables just aren't as common in languages like Clojure.
I have an atom called app-state that holds a map. It looks like this:
{:skills [{:id 1 :text "hi"} {:id 2 :text "yeah"}]}
What is the idiomatic way to remove the element inside the vector with :id = 2 ? The result would look like:
{:skills [{:id 1 :text "hi"}]}
...
So far, I have this:
(defn new-list [id]
(remove #(= id (:id %)) (get #app-state :skills)))
swap! app-state assoc :skills (new-list 2)
It works, but I feel like this isn't quite right. I think it could be something like:
swap! app-state update-in [:skills] remove #(= id (:id %))
But this doesn't seem to work.
Any help is much appreciated!
Try this:
(defn new-list [app-state-map id]
(assoc app-state-map :skills (into [] (remove #(= id (:id %)) (:skills app-state-map)))))
(swap! app-state new-list 2)
swap! will pass the current value of the atom to the function you supply it. There's no need to dereference it yourself in the function.
See the docs on swap! for more details.
(swap! state update :skills (partial remove (comp #{2} :id)))
(def skills {:skills [{:id 1 :text "hi"} {:id 2 :text "yeah"}]})
(defn remove-skill [id]
(update skills :skills (fn [sks] (vec (remove #(= id (:id %)) sks)))))
You would then be able to call say (remove-skill 1) and see that only the other one (skill with :id of 2) is left.
I like your way better. And this would need to be adapted for use against an atom.
You can use filter to do this. Here is a function that takes an id and the map and let's you filter out anything that doesn't match your criteria. Of course, you can make the #() reader macro check for equality rather than inequality depending on your needs.
user=> (def foo {:skills [{:id 1 :text "hi"} {:id 2 :text "yeah"}]})
#'user/foo
user=> (defn bar [id sklz] (filter #(not= (:id %) id) (:skills sklz)))
#'user/bar
user=> (bar 1 foo)
({:id 2, :text "yeah"})
user=> (bar 2 foo)
({:id 1, :text "hi"})
It's become an obsession of mine to reduce the lines of code that I write in Lisp/Clojure. I am trying to make the following code (essentially a depth first search) shorter.
(defn find-node [nodeid in-node]
(if (= nodeid (:id in-node))
in-node
(loop [node nil activities (:activities in-node)]
(if (or (empty? activities) (not (nil? node)))
node
(recur (find-node nodeid (first activities)) (rest activities))))))
(defn find-node-in-graph [nodeid node activities]
(if (empty? activities)
node
(recur nodeid (find-node nodeid (first activities)) (rest activities))))
(defrecord Graph [id name activities])
(defrecord Node [id name activities])
"activities" is a list
This might be cheating, though it's only one line ;)
(def tree {:id 1 :children [{:id 2 :children [{:id 3}]} {:id 4}]})
core> (filter #(= 3 (:id %)) (tree-seq :children :children tree))
({:id 3})
core> (filter #(= 2 (:id %)) (tree-seq :children :children tree))
({:children [{:id 3}], :id 2})
Some points on the original intent of the question:
loop/recer is often not the most compact form, it wan usually be written with map or doseq
if/recur can often be replaced with a call to filter
If you seem to really need to write something like this because of some specific requirement, often zippers will solve the problem more elegantly.
Imagine you have a map like this:
(def person {
:name {
:first-name "John"
:middle-name "Michael"
:last-name "Smith" }})
What is the idiomatic way to change values associated with both :first-name and :last-name in one expression?
(Clarification: Let's say you want to set :first-name to "Bob" and :last-name to "Doe". Let's also say that this map has some other values in it that we want to preserve, so constructing it from scratch is not an option)
Here are a couple of ways.
user> (update-in person [:name] assoc :first-name "Bob" :last-name "Doe")
{:name {:middle-name "Michael", :last-name "Doe", :first-name "Bob"}}
user> (update-in person [:name] merge {:first-name "Bob" :last-name "Doe"})
{:name {:middle-name "Michael", :last-name "Doe", :first-name "Bob"}}
user> (update-in person [:name] into {:first-name "Bob" :last-name "Doe"})
{:name {:middle-name "Michael", :last-name "Doe", :first-name "Bob"}}
user> (-> person
(assoc-in [:name :first-name] "Bob")
(assoc-in [:name :last-name] "Doe"))
{:name {:middle-name "Michael", :last-name "Doe", :first-name "Bob"}}
Edit
update-in does recursive assocs on your map. In this case it's roughly equivalent to:
user> (assoc person :name
(assoc (:name person)
:first-name "Bob"
:last-name "Doe"))
The repetition of keys becomes more and more tedious as you go deeper into a series of nested maps. update-in's recursion lets you avoid repeating keys (e.g. :name) over and over; intermediary results are stored on the stack between recursive calls. Take a look at the source for update-in to see how it's done.
user> (def foo {:bar {:baz {:quux 123}}})
#'user/foo
user> (assoc foo :bar
(assoc (:bar foo) :baz
(assoc (:baz (:bar foo)) :quux
(inc (:quux (:baz (:bar foo)))))))
{:bar {:baz {:quux 124}}}
user> (update-in foo [:bar :baz :quux] inc)
{:bar {:baz {:quux 124}}}
assoc is dynamic (as are update-in, assoc-in, and most other Clojure functions that operate on Clojure data structures). If assoc onto a map, it returns a map. If you assoc onto a vector, it returns a vector. Look at the source for assoc and take a look in in RT.java in the Clojure source for details.
I'm not sure if my case is quite the same but I had list of changes to apply:
(def updates [[[:name :first-name] "Bob"]
[[:name :last-name] "Doe"]])
In that case you can reduce over that list with assoc-in like this:
(reduce #(assoc-in %1 (first %2) (second %2)) person updates)