Clojure - named arguments - clojure

Does Clojure have named arguments? If so, can you please provide a small example of it?

In Clojure 1.2, you can destructure the rest argument just like you would destructure a map. This means you can do named non-positional keyword arguments. Here is an example:
user> (defn blah [& {:keys [key1 key2 key3]}] (str key1 key2 key3))
#'user/blah
user> (blah :key1 "Hai" :key2 " there" :key3 10)
"Hai there10"
user> (blah :key1 "Hai" :key2 " there")
"Hai there"
user> (defn blah [& {:keys [key1 key2 key3] :as everything}] everything)
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
{:key2 " there", :key1 "Hai"}
Anything you can do while destructuring a Clojure map can be done in a function's argument list as shown above. Including using :or to define defaults for the arguments like this:
user> (defn blah [& {:keys [key1 key2 key3] :or {key3 10}}] (str key1 key2 key3))
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
"Hai there10"
But this is in Clojure 1.2. Alternatively, in older versions, you can do this to simulate the same thing:
user> (defn blah [& rest] (let [{:keys [key1 key2 key3] :or {key3 10}} (apply hash-map rest)] (str key1 key2 key3)))
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
"Hai there10"
and that works generally the same way.
And you can also have positional arguments that come before the keyword arguments:
user> (defn blah [x y & {:keys [key1 key2 key3] :or {key3 10}}] (str x y key1 key2 key3))
#'user/blah
user> (blah "x" "Y" :key1 "Hai" :key2 " there")
"xYHai there10"
These are not optional and have to be provided.
You can actually destructure the rest argument like you would any Clojure collection.
user> (defn blah [& [one two & more]] (str one two "and the rest: " more))
#'user/blah
user> (blah 1 2 "ressssssst")
"12and the rest: (\"ressssssst\")"
You can do this sort of thing even in Clojure 1.1. The map-style destructuring for keyword arguments only came in 1.2 though.

In addition to Raynes' excellent answer, there is also a macro in clojure-contrib that makes life easier:
user=> (use '[clojure.contrib.def :only [defnk]])
nil
user=> (defnk foo [a b :c 8 :d 9]
[a b c d])
#'user/foo
user=> (foo 1 2)
[1 2 8 9]
user=> (foo 1 2 3)
java.lang.IllegalArgumentException: No value supplied for key: 3 (NO_SOURCE_FILE:0)
user=> (foo 1 2 :c 3)
[1 2 3 9]

As of Clojure version 1.8, keyword support still seems a bit meh.
You can specify keyword arguments like this:
(defn myfn1
"Specifying keyword arguments without default values"
[& {:keys [arg1 arg2]}]
(list arg1 arg2))
Examples of calling it:
(myfn1 :arg1 23 :arg2 45) --> evaluates to (23 45)
(myfn1 :arg1 22) --> evaluates to (22 nil)
If you want to specify default values for these keyword arguments:
(defn myfn2
"Another version, this time with default values specified"
[& {:keys [arg1 arg2] :or {arg1 45 arg2 55}}]
(list arg1 arg2))
This does the expected thing in the second case:
(myfn2 :arg1 22) --> evaluates to (22 55)
There are pros and cons to each part of each language, but just for comparison, this is how you would do the same stuff in Common Lisp:
(defun myfn3
(&key arg1 arg2)
"Look Ma, keyword args!"
(list arg1 arg2))
(defun myfn4
(&key (arg1 45) (arg2 55))
"Once again, with default values"
(list arg1 arg2))

Do you perhaps mean named parameters? These aren't directly available, but you can use this vectors approach if you like, which may give you what you want.
At RosettaCode there's a deeper explanation on how to do this using destructuring.

Related

Compact way in Clojure to print a variable number of command line arguments?

I'm a newbie to Clojure. I think I'm trying to solve this procedurally and there must be a better (more functional) way to do this in Clojure...
The -main function can receive a variable number of 'args'. I would like to print them out but avoid an IndexOutOfBoundsException. I thought I could mimic Java and use a case fall-through to minimize the code involved, but that didn't work:
(defn -main [& args]
(println "There are" (str (count args)) "input arguments.")
(println "Here are args:" (str args))
(let [x (count args)]
(case x
(> x 0) (do
(print "Here is the first arg: ")
(println (nth args 0)))
(> x 1) (do
(print "Here is the 2nd arg: ")
(println (nth args 1)))
(> x 2) (do
(print "Here is the 3rd arg: ")
(println (nth args 2))))))
(doseq [[n arg] (map-indexed vector arguments)]
(println (str "Here is the argument #" (inc n) ": " (pr-str arg))))
map-indexes is like map but adds index number in the beginning.
So it goes item by item through arguments, packs index and item into a vector and by destructruing index number and item are mapped to [n arg].
Since clojure begins counting from 0, you use (inc n) to begin counting from 1. pr-str is pretty print string. The str joins all string components together.
there is also a handy formatting facility in clojure's core library: cl-format, which is the port of common lisp's format syntax. It includes a nice way to print out collections:
(require '[clojure.pprint :refer [cl-format]])
(let [args [:a :b :c :d]]
(cl-format true "~{here is the ~:r arg: ~a~%~}"
(interleave (rest (range)) args)))
;; here is the first arg: :a
;; here is the second arg: :b
;; here is the third arg: :c
;; here is the fourth arg: :d
some more information about what format can do is here

Return concatenated params in clojure

Need function or macro, which takes const parameter(route) and dynamic parameters (args) and return concatenated string of parameters:
user>(defn full-url [route & args] *need code* )
#'user/full-url
user> (def p1 "value1")
#'user/p1
user> (def p2 "value2")
#'user/p2
user> (def p3 "value3")
#'user/p3
user> (full-url "/init" p1 p2 p3)
"/init?p1=value1&p2=value2&p3value4"
Any ideas?
First, a macro to do what you want:
(defmacro full-url
[route & args]
`(let [var-names# (map #(str %1 "=") '~args)
var-vals# (list ~#args)
joined# (clojure.string/join "&" (map str var-names# var-vals#))]
(str ~route "?" joined#)))
Now, I would add that I do not think this is the best approach as it ties the names of your vars to the param names. IMO a better approach is to use a regular function that takes a map as a second argument, that has keywords and values. Such as:
(defn full-url-fn
[route params]
(->> params
(map #(str (name (first %)) "=" (second %)))
(clojure.string/join "&")
(str route "?")))
(full-url-fn "/init" {:p1 "value1" :p2 "value2"})
But, either way should work.

vector in clojure and type casting

(= ":bar:foo" ((fn [[a b]] (str b a)) [:foo :bar]))
I have several question about this clojure code.
what's the deal about : in front each element in vector?
How can str cast :foo to string type ":foo" ?
Thanks
In clojure, such element called as keywords. Keywords evaluate to themselves, and often used as accessors for the values.
(def x {:a 10, :b 20})
You can check the type:
user=> (class :foo)
clojure.lang.Keyword
user=> (type :foo)
clojure.lang.Keyword
You can convert it to str: Be cautious that : in the front.
user=> (str :foo)
":foo"
If you want to get only the name string from the keyword, then:
user=> (name :foo)
"foo"
Or you can create a keyword from str:
user=> (keyword "foo")
:foo

Best way to update several values in hashmap?

I have a hash map like this:
{:key1 "aaa bbb ccc" :key2 "ddd eee" :key3 "fff ggg" :do-not-split "abcdefg hijk"}
And I'd like to split some of the strings to get vectors:
; expected result
{:key1 ["aaa" "bbb" "ccc"] :key2 ["ddd" "eee"] :key3 ["fff" "ggg"] :do-not-split "abcdefg hijk"}
I use update-in three times now like the following but it seems ugly.
(-> my-hash (update-in [:key1] #(split % #"\s"))
(update-in [:key2] #(split % #"\s"))
(update-in [:key3] #(split % #"\s")))
I hope there's sth like (update-all my-hash [:key1 :key2 :key3] fn)
You can use reduce:
user=> (def my-hash {:key1 "aaa bbb ccc" :key2 "ddd eee" :key3 "fff ggg"})
#'user/my-hash
user=> (defn split-it [s] (clojure.string/split s #"\s"))
#'user/split-it
user=> (reduce #(update-in %1 [%2] split-it) my-hash [:key1 :key2 :key3])
{:key3 ["fff" "ggg"], :key2 ["ddd" "eee"], :key1 ["aaa" "bbb" "ccc"]}
Just map the values based on a function that makes the decision about whether to split or not.
user=> (def x {:key1 "aaa bbb ccc"
:key2 "ddd eee"
:key3 "fff ggg"
:do-not-split "abcdefg hijk"})
#'user/x
user=> (defn split-some [predicate [key value]]
(if (predicate key)
[key (str/split value #" ")]
[key value]))
#'user/split-some
user=> (into {} (map #(split-some #{:key1 :key2 :key3} %) x))
{:do-not-split "abcdefg hijk", :key3 ["fff" "ggg"], :key2 ["ddd" "eee"], :key1 ["aaa" "bbb" "ccc"]}
This is a different way of approaching the problem.
Think about it for a second: if your string were in a list, how would you approach it?
The answer is that you would use map to get a list of vectors:
(map #(split % #"\s") list-of-strings)
If you think harder you would arrive at the conclusion that what you really want is to map a function over the values of a map. Obviously map doesn't work here as it works for sequences only.
However, is there a generic version of map? It turns out there is! It's called fmap and comes from the concept of functors which you can ignore for now. This is how you would use it:
(fmap my-hash #(split % #"\s"))
See how the intent is a lot clearer now?
The only drawback is that fmap isn't a core function but it is available through the algo.generic library.
Of course if including a new library feels like too much at this stage, you can always steel the source code - and attribute to its author - from the library itself in this link:
(into (empty my-hash) (for [[k v] my-hash] [k (your-function-here v)]))

In Clojure how can I pass multiple arguments to a defmethod?

I wish to create a multi method which I call like this:
(defmethod some-method "some value"
[ a b ]
b)
: but which selects the function based only on the first paramter 'a'. How can I do this:
(defmulti some-method
WHAT GOES HERE?)
I didn't completely understand your question, but I think you want to
dispatch only on one argument. You can do that like this, I think:
user=> (defmulti even-or-odd (fn [x _] (even? x)))
#'user/even-or-odd
user=> (defmethod even-or-odd true [a _] :even)
#<MultiFn clojure.lang.MultiFn#293bdd36>
user=> (defmethod even-or-odd false [a _] :odd)
#<MultiFn clojure.lang.MultiFn#293bdd36>
user=> (even-or-odd 2 3)
:even
user=> (even-or-odd 3 3)
:odd
user=>
Do you mean select the function based on the value of a?
Then you just need
(defmulti some-method (fn [a b] a))