I define a function, which takes two parameters - map and a key. The key is referenced from the map parameter decomposition
(defn myfunc [{v k} k]
v)
when I call:
(myfunc {:a 10} :a)
It surprisingly produces expected result: 10
Similar thing in the let:
(let [{v k} {:a 10} k :a] v)
fails, because k is not defined at the moment, when first part is evaluated.
My question is: why decomposition inside function parameters behaves differently compared to decomposition in let expressions?
Macroexpanding the defn form I get the equivalent of this (removed .withMeta stuff and reformatted):
(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))))
So here we can see that the {v k} map is in fact first assigned to a local variable p__11393. Then the if statement tests if that variable is in fact a sequence and turns it into a hash-map if so, otherwise leaves it as it is. Importantly, the value assigned to k is looked up in the map after all of this happens, thus this works without error (and also would if :a wasn't in the map, get returns nil in that case).
On the other hand macroexpanding the let form I get
(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)
and here we can see that v gets the result of (get map__11431 k), but k isn't defined at this point yet, hence the error.
This isn't a complete answer, but the following does work:
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=>
Related
I am trying to do something similar in Clojure:
# Increase the count of a feature/category pair
def incf(self,f,cat):
self.fc.setdefault(f,{})
self.fc[f].setdefault(cat,0)
self.fc[f][cat]+=1
Does anybody have idea?
I think a close parallel is fnil which takes a function and some default argument value(s), then returns a function that will use the default value if called with nil argument(s):
(defn incf [fc f cat]
(update-in fc [f cat] (fnil inc 0)))
(incf {} :feature-foo :category-bar)
=> {:feature-foo {:category-bar 1}}
Here we use update-in to update a nested value in the input map, and use fnil to set the default value zero to be incremented if it doesn't exist.
First, you cannot mutate a hash map in Clojure, so there is no exact counterpart for setdefault.
However, if you want to update a nested map increasing the given value (or set to 1, if it is null), you can leverage the fact that clojure.core/get can accept an optional third argument which is the default value (and also, an assoc on nil creates a map):
(defn incf [m cat f]
(let [val (get-in m [cat f] 0)]
(assoc-in m [cat f] (inc val))))
(incf {:my-cat {:a 1}} :my-cat :a) ; returns {:my-cat {:a 2}}
(incf {:my-cat {}} :my-cat :a) ; returns {:my-cat {:a 1}}
(incf {} :my-cat :a) ; returns {:my-cat {:a 1}}
I would like to update a simple atom, like: {:a 1} and return new value just like swap! does, only if given key already exists (and return nil otherwise).
This is the simplest solution I figured out:
(defn cond-assoc [store k v]
(when (get #store k)
(swap! store assoc k v)))
but I don't feel it's really atomic. In the wild lot of things may happen between (get #store k) and the swap! operations. Is there any better solution than that?
You are right, your solution is not very atomic. To make an atomic swap, make a simple function that transforms the map only when required:
(defn assoc-if-exists [m k v]
(if (m k) (assoc m k v) m))
Note that this has nothing to do with an atom or with being atomic. To use it with an atom, simply do
(def store (atom {:a 1}))
(swap! store assoc-if-exists :a 4) ;; {:a 4}, value changed
(swap! store assoc-if-exists :b 4) ;; {:a 4}, nothing added
Notice that this does not return nil when the key does not exists. If you want that behavior, you could check if the returned value contains the key:
(defn atomic-assoc-or-nil [a k v]
(let [r (swap! a assoc-if-exists k v)]
(when (get r k) r)))
I've been learning Clojure for a few weeks now. I know the basics of the data structures and some functions. (I'm reading the Clojure Programming book).
I'm stuck with the following. I'm writing a function which will lower case the keys of the supplied map.
(defn lower-case-map [m]
(def lm {})
(doseq [k (keys m)]
(assoc lm (str/lower-case k) (m k))))
This does what I want, but how do I return the map? Is the def correct?
I know this works
(defn lower-case-map [m]
(assoc {} :a 1))
But the doseq above seems to be creating a problem.
Within a function body you should define your local variables with let, yet this code looks alot like you try to bend it into an imperative mindset (def tempvar = new Map; foreach k,v in m do tempvar[k.toLower] = v; return tempvar). Also note, that the docs of doseq explicitly state, that it returns nil.
The functional approach would be a map or reduce over the input returning the result directly. E.g. a simple approach to map (iterating the sequence of elements, destructure the key/value tuple, emit a modified tuple, turn them back into a map):
user=> (into {} (map (fn [[k v]] [(.toLowerCase k) v]) {"A" 1 "B" 2}))
{"a" 1, "b" 2}
For your use-case (modify all keys in a map) is already a nice core function: reduce-kv:
user=> (doc reduce-kv)
-------------------------
clojure.core/reduce-kv
([f init coll])
Reduces an associative collection. f should be a function of 3
arguments. Returns the result of applying f to init, the first key
and the first value in coll, then applying f to that result and the
2nd key and value, etc. If coll contains no entries, returns init
and f is not called. Note that reduce-kv is supported on vectors,
where the keys will be the ordinals.
user=> (reduce-kv (fn [m k v] (assoc m (.toLowerCase k) v)) {} {"A" 1 "B" 2})
{"a" 1, "b" 2}
I'm getting unexpected behaviour in some monads I'm writing.
I've created a parser-m monad with
(def parser-m (state-t maybe-m))
which is pretty much the example given everywhere (here, here and here)
I'm using m-plus to act as a kind of fall-through query mechanism, in my case, it first reads values from a cache (database), if that returns nil, the next method is to read from "live" (a REST call).
However, the second value in the m-plus list is always called, even though its value is disgarded (if the cache hit was good) and the final return is that of the first monadic function.
Here's a cutdown version of the issue i'm seeing, and some solutions I found, but I don't know why.
My questions are:
Is this expected behaviour or a bug in m-plus? i.e. will the 2nd method in a m-plus list always be evaluated even if the first item returns a value?
Minor in comparison to the above, but if i remove the call
_ (fetch-state) from checker, when i evaluate that method, it
prints out the messages for the functions the m-plus is calling
(when i don't think it should). Is this also a bug?
Here's a cut-down version of the code in question highlighting the problem. It simply checks key/value pairs passed in are same as the initial state values, and updates the state to mark what it actually ran.
(ns monads.monad-test
(:require [clojure.algo.monads :refer :all]))
(def parser-m (state-t maybe-m))
(defn check-k-v [k v]
(println "calling with k,v:" k v)
(domonad parser-m
[kv (fetch-val k)
_ (do (println "k v kv (= kv v)" k v kv (= kv v)) (m-result 0))
:when (= kv v)
_ (do (println "passed") (m-result 0))
_ (update-val :ran #(conj % (str "[" k " = " v "]")))
]
[k v]))
(defn filler []
(println "filler called")
(domonad parser-m
[_ (fetch-state)
_ (do (println "filling") (m-result 0))
:when nil]
nil))
(def checker
(domonad parser-m
[_ (fetch-state)
result (m-plus
;; (filler) ;; intitially commented out deliberately
(check-k-v :a 1)
(check-k-v :b 2)
(check-k-v :c 3))]
result))
(checker {:a 1 :b 2 :c 3 :ran []})
When I run this as is, the output is:
> (checker {:a 1 :b 2 :c 3 :ran []})
calling with k,v: :a 1
calling with k,v: :b 2
calling with k,v: :c 3
k v kv (= kv v) :a 1 1 true
passed
k v kv (= kv v) :b 2 2 true
passed
[[:a 1] {:a 1, :b 2, :c 3, :ran ["[:a = 1]"]}]
I don't expect the line k v kv (= kv v) :b 2 2 true to show at all. The final result is the value returned from the first function to m-plus, as I expect, but I don't expect the second function to even be called.
Now, I've found if I pass a filler into m-plus that does nothing (i.e. uncomment the (filler) line) then the output is correct, the :b value isn't evaluated.
If I don't have the filler method, and make the first method test fail (i.e. change it to (check-k-v :a 2) then again everything is good, I don't get a call to check :c, only a and b are tested.
From my understanding of what the state-t maybe-m transformation is giving me, then the m-plus function should look like:
(defn m-plus
[left right]
(fn [state]
(if-let [result (left state)]
result
(right state))))
which would mean that right isn't called unless left returns nil/false.
Edit:
After looking at state-t and maybe-m source, the m-plus looks more like:
(fn [& statements]
(fn [state]
(apply (fn [& xs]
(first (drop-while nil? xs)))
(map #(% state) statements))))
But the principle is the same, (first (drop-while nil? ...) should only execute over the items that return a valid value.
I'd be interested to know if my understanding is correct or not, and why I have to put the filler method in to stop the extra evaluation (whose effects I don't want to happen).
Edit:
If I switch over to using Jim Duey's hand written implementation of parser-m (from his excellent blogs), there is no evaluation of the second function in m-plus, which seems to imply the transformation monad is breaking m-plus. However, even in this implementation, if I remove the initial (fetch-state) call in the checker function, the domonad definition causes the output of the creation of the m-plus functions, suggesting something going on in domonad's implementation I'm not expecting.
Apologies for the long winded post!
I'm working my way through 4clojure and I'm stuck on Problem 156 (Map Defaults).
I can't figure out why the function bellow doesn't return a flat map
((fn [d k] (for [i k :let [r {}]]
(conj r [i d])))
[:a :b] [:foo :bar])
Current result is ({:foo [:a :b]} {:bar [:a :b]})
But I expected {:foo [:a :b], :bar [:a :b]}
Inside for, r is created anew in every iteration, gets populated with [i d] and gets yielded as an element of the lazy sequence. As a result, you obtain this sequence whose elements are small one-entry maps.
What you need is reduce. It loops over a sequence updating the accumulator using a function you provide:
(defn fun1 [d k]
(reduce
(fn [acc i] (conj acc [i d]))
{}
k))
It starts from an empty map, and for every element i in k it calls the lambda, which adds an entry to the map (passed to the lambda as acc). The result is one big map with all these entries.
Alternatively, you could just generate the key/value pairs with your for expression, and then use the into function to shove them all in a map:
((fn [d k] (into {} (for [i k] [i d])))
[:a :b] [:foo :bar])
; => {:foo [:a :b], :bar [:a :b]}
For those coming here looking for a flatmap function in Clojure, check out mapcat:
Returns the result of applying concat to the result of applying map to
f and colls.