I have a Clojure function with parameter: [{:keys [from to]}]. Both the from and to keys are optional. I would like to remap these keys into new keys but maintaining the values. What's an efficient/idiomatic way to go about this? Here's my curent solution.
(defn query
[{:keys [from to]}]
(let [re-map {:$gte from
:$lt to}]
(into {} (remove #(nil? (val %)) re-map))))
I need it to return either one or both, and no nil if one of the key wasn't entered. So
=>(query {:from 10})
{:$gte 10}
and
=>(query {:from 10 :to 20})
{:$gte 10 :lt 20}
There is a function for this in the clojure.set namespace called rename-keys:
user=> (use 'clojure.set)
nil
user=> (rename-keys {:from 10} {:from :$gte :to :$lt})
{:$gte 10}
You can use destructuring for this:
user=> (defn re-map [{the-x :x the-y :y}] the-x)
user=> (re-map {:x 10 :y 1})
10
I would recommend this excellent introduction with lot of examples.
Related
In clojure, to edit the values of the keys of a map, there are 2 options available:
update
assoc
The only difference that I could find from the clojure documentation is that the update function does the following extra thing:
If the key does not exist, nil is passed as the old value.
Are there any specific use cases where in I should use update or assoc other than this?
Or in which cases I should prefer what (If I have missed these use cases).
They both state clear intent in your code when used properly:
assoc hints to the reader, that new kv-pairs are added (or
replacing the values for existing keys) and that they tend to not be
relying on the current values for that keys.
update hints, that one value for a key gets updated and the original
value (if present) maybe used for this calculation.
(update {} :a (fnil inc 0))
; → {:a 1}
(update {:a 41} :a (fnil inc 0))
; → {:a 42}
(assoc {:z 0} :a 42 :b 23}
; → {:z 0, :a 42, :b 23}
You an emulate update with assoc as follows
(assoc m :foo (f (:foo m)))
which looks clunky and is better off as:
(update m :foo f)
which returns a new map with f applied on the the value corresponding to the :foo key.
The uses that I know so far are on vectors:
(get [1 2 3 4] 2) ; => 3
and on maps:
(get {:a "a" :b "B" :c "c"} :c) ; => "c"
From documentation it says:
clojure.core/get ([map key] [map key not-found])
Returns the value mapped to key, not-found or nil if key not present.
Apart from maps and vectors, a common use for get is on strings:
(get "Cthulhu" 2) ;; => \h
get also works on sets and native Java(Script) arrays. One possible use in ClojureScript and JavaScript inter-op:
(def js-array (-> (js/Array 10) ;Create and fill native JS array
(.fill "a")
(.map (fn [_ i] i))))
(get js-array 3) ; => 3
As another example, get works for looking up by item in a set:
(get #{:b :c :a} :c) ;;=> :c
Note that it does not work with a (sorted) set and index, for example:
(get (sorted-set :b :a :c) 1) ;; throws exception
Further, maps, vectors, and sets act as functions of their members, so you can often avoid using get altogether:
(#{:a :b :c} :b) ; => :b
({:a 1 :b 2 :c 3} :b) ; => 2
([:a :b :c] 1) ; => :b
The advantage of using get with them is that you can provide a default value:
(get {:a :b :c} :d) ; => nil
(get {:a :b :c} :d :not-found) ; => :not-found
See also #Thumbnail's answer to understand how get works under the hood.
Further to #ToniVanhanla's answer, for the JVM, the relevant Clojure interface is clojure.lang.ILookup.
Looking, as the Americans say, under the hood,
Clojure get translates into a call to clojure.lang.RT/get.
If possible, this casts to ILookup and calls the appropriate
valAt method.
If not, it calls ...RT/getFrom
... whose clauses deal explicitly with, in turn,
Java maps,
Clojure sets, and
Java strings and arrays.
If none of these fits, it returns nil.
There is no parent interface for Java arrays: they all descend directly from Object. They are detected by Java's Class/isArray
Somewhat surprisingly, Clojure get does not work on Java collections such as Vectors:
(java.util.Vector. (seq "Hello, world!"))
=> [\H \e \l \l \o \, \space \w \o \r \l \d \!]
But
(get (java.util.Vector. (seq "Hello, world!")) 4)
=> nil
I know how to create mutable vector:
(defn create-vector []
(intern *ns* (symbol "my-vector" (ref []))
=>my-namespace/my-vector
I know how to add element to that vector:
(dosync (alter my-vector conj "test"))
=>["test"]
Now I have "test" string in my mutable vector. But how I can remove it? I tried to use lazy-sequence function remove
(dosync (alter my-vector remove "test"))
but it doesn't work. How can I remove element from mutable vector?
EDIT:
The collection doesn't really have to be vector. I found solution for set, but will wait if someone will suggest solution for vector.
your mistake is that you pass the "test" arg to remove function, while it really accepts a predicate. In your case this one wold work (unproperly though, making a list instead of a vector):
(dosync (alter my-vector #(remove #{"test"} %)))
to keep it a vector you would have to do this:
(dosync (alter my-vector #(vec (remove #{"test"} %))))
what makes me curious about your example:
why would you use this weird construction:
(defn create-vector []
(intern *ns* (symbol "my-vector" (ref []))))
instead of just (def my-vector (ref []))
why do you use ref at all? would you be using it inside some transaction? if not, i would propose you to move to an atom (since it is also thread safe)
user> (def my-atom (atom []))
#'user/my-atom
user> (swap! my-atom conj "hello")
["hello"]
user> (swap! my-atom #(remove #{"hello"} %))
()
As specified in my edit, collection doesn't have to be really vector. #leetwinski posted answer how to do it for vector, but if I would decide to use set I found other solution. So if we create set the same way as in question replacing [] to #{}:
(defn create-vector []
(intern *ns* (symbol "my-set" (ref #{}))
=>my-namespace/my-vector
and add element:
(dosync (alter my-set conj "test"))
=>#{"test"}
, we can remove it
(dosync (alter my-set disj item))
=>#{}
I'd prefer examples to be in a Lisp variant (bonus points for Clojure or Scheme) since that's what I'm most familiar with, but any feedback regarding DBC in functional lanugages would of course be valuable to the greater community.
Here's an obvious way:
(defn foo [action options]
(when-not (#{"go-forward" "go-backward" "turn-right" "turn-left"} action)
(throw (IllegalArgumentException.
"unknown action")))
(when-not (and (:speed options) (> (:speed options) 0))
(throw (IllegalArgumentException.
"invalid speed")))
; finally we get to the meat of the logic)
What I don't like about this implementation is that the contract logic obscures the core functionality; the true purpose of the function is lost in conditional checks. This is the same issue I raised in this question. In an imperative language like Java, I can use annotations or metadata/attributes embedded in documentation to move the contract out of the method implementation.
Has anyone looked at adding contracts to metadata in Clojure? How would higher-order functions be used? What other options are there?
Clojure already has support for pre and post conditions, unfortunately not well documented:
Should I use a function or a macro to validate arguments in Clojure?
I could imagine something like this in Clojure:
(defmacro defnc
[& fntail]
`(let [logic# (fn ~#(next fntail))]
(defn ~(first fntail)
[& args#]
(let [metadata# (meta (var ~(first fntail)))]
(doseq [condition# (:preconditions metadata#)]
(apply condition# args#))
(let [result# (apply logic# args#)]
(doseq [condition# (:postconditions metadata#)]
(apply condition# result# args#))
result#)))))
(defmacro add-pre-condition!
[f condition]
`(do
(alter-meta! (var ~f) update-in [:preconditions] conj ~condition)
nil))
(defmacro add-post-condition!
[f condition]
`(do
(alter-meta! (var ~f) update-in [:postconditions] conj ~condition)
nil))
An example session:
user=> (defnc t [a test] (a test))
\#'user/t
user=> (t println "A Test")
A Test
nil
user=> (t 5 "A Test")
java.lang.ClassCastException: java.lang.Integer (NO_SOURCE_FILE:0)
user=> (add-pre-condition! t (fn [a _] (when-not (ifn? a) (throw (Exception. "Aaargh. Not IFn!")))))
nil
user=> (t 5 "A Test")
java.lang.Exception: Aaargh. Not IFn! (NO_SOURCE_FILE:0)
user=> (t println "A Test")
A Test
nil
So you can define the function and then afterwards define pre- and post-conditions whereever you like, without cluttering the function logic itself.
condition functions should throw an exception if something is wrong.
When I paste this code into a REPL, it works fine:
(use 'clojure.contrib.seq-utils)
(defn- random-letter [] (char (+ (rand-int 26) 97)))
(defn- random-digit [] (rand-int 10))
(defn- random-password
"Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
[]
(let [password (interpose '((random-digit)) (repeat 3 (repeat 2 '(random-letter))))]
(apply str (flatten (map (fn [coll] (map eval coll)) password)))))
Now, I have this code with :gen-class :implements [my.ServiceInterface] and a function prefixed with - to implement the interface. I unit-test with Maven/Groovy/TestNG. Everything works fine with several other interfaces/Clojure implementations, but in this particular case, I get this error:
java.lang.RuntimeException:
java.lang.Exception: Unable to resolve symbol: random-letter in this context (NO_SOURCE_FILE:32)
I can't figure out why. The only thing I can tell is different in this function from all the other functions, is that this is the only place where I use quoting, i.e. '((random-digit)) and '(random-letter). EDIT: also, this is the only place where I use eval.
I tried defining the functions as "non-private" (defn instead of defn-). I also tried a (declare random-digit random-letter) at the top. Neither of these solves the problem.
On a side note, if you have a suggestion for a better way to implement the random-password function, I am all ears. But I'd still like to know why I am getting this error and how to get this to work.
Many thanks in advance for your help. Clojure is awesome.
Edit: here is the complete code.
(ns fred.hp2010.service.ClojurePoolerService
(:gen-class :implements [fred.hp2010.service.PoolerService])
(:use [clojure.contrib.seq-utils :only (flatten)]))
(def dao (fred.hp2010.persistence.Repository/getDao))
(declare find-by is-taken random-password)
(defn -addPooler [this pooler] (. dao insert "POOLER" pooler))
(defn -getPoolers [this] (. dao list "poolers"))
(defn -isEmailTaken [this email] (is-taken {"email" email}))
(defn -isUsernameTaken [this username] (is-taken {"username" username}))
(defn -login [this email password] (. dao findSingle "POOLER" {"email" email "password" password}))
(defn -changePassword [this email new-password]
(let [updated-pooler (assoc (into {} (find-by {"email" email})) "password" new-password)]
(. dao update "POOLER" "POOLER_ID" updated-pooler)))
(defn -resetPassword [this email]
(let [new-password (random-password)]
(-changePassword this email new-password)
new-password))
(defn- find-by [params] (. dao findSingle "POOLER" params))
(defn- is-taken [params] (not (nil? (find-by params))))
(defn- random-letter [] (char (+ (rand-int 26) 97)))
(defn- random-digit [] (rand-int 10))
(defn- random-password
"Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
[]
(let [password (interpose '((random-digit)) (repeat 3 (repeat 2 '(random-letter))))]
(apply str (flatten (map (fn [coll] (map eval coll)) password)))))
I don't know why you're having problems compiling this with :gen-class but I wouldn't be surprised if eval had something to do with it. eval is usually a bad idea. One thing to try (completely untested) is to use ` (backquote) instead of ' (quote) so that your symbols are namespace-qualified. Don't know if that'd help or not.
Probably better to get rid of eval though. If you turn your random-character functions into infinite lazy seqs via repeatedly you can do it this way:
(defn- random-letter [] (repeatedly #(char (+ (rand-int 26) 97))))
(defn- random-digit [] (repeatedly #(rand-int 10)))
(defn- random-password
"Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
[]
(apply str
(mapcat (fn [[n f]] (take n (f)))
[[2 random-letter]
[1 random-digit]
[2 random-letter]
[1 random-digit]
[2 random-letter]])))
In the top handful of lines, I'm having a bit of trouble following the syntax. In particular, why all the quotes in line 7? Delayed evaluation of all those expressions is probably not helping you. I would guess that the quoted '(random-letter) is spoiling your fun.
You can probably write simpler code while eschewing eval. I'm going to go try it in the REPL, I hope to be back soon with an improved version.
EDIT:
OK, this works:
(apply str (interpose (random-digit) (repeat 3 (apply str (repeat 2 (random-letter))))))
...and it doesn't need anything from clojure.contrib :)
The str function will mung any arguments together into a string. If the arguments are in a list, you smuggle str inside the list by using apply.
As you said, Clojure is cool!
EDIT:
Here's a function that generates a random string of alphas and numerics in accordance with a String specification:
(apply str (map (fn [c] (if (= c \a) (random-letter) (random-digit))) "aanaanaa")))
It deviates a little bit from your spec but I think it's pretty cool.