In JavaScript, one can do the following:
function foo(arg1, arg2, arg3) {
...
}
var others = [ 'two', 'three' ];
foo('one', ...others); // same as foo('one', 'two', 'three')
In Clojure, "variable args" can be accepted like so:
(defn foo [arg1 & others]
...)
But to pass them in combination with other args, you have to do this:
(apply foo (concat '("one") others))
Which is frankly really ugly. It's also impossible when what you need to do is recur:
(apply recur (concat '("one") others)) ;; doesn't work
Is there a better way to do this? And if not, is there any way at all to accomplish it in the recur case?
But to pass them in combination with other args, you have to do this:
(apply foo (concat '("one") others))
You don't have to do that: apply is also a variadic function that can take arguments before the final sequence argument e.g.
(apply foo "one" others)
You can pass any number of individual arguments before the final sequence argument to apply.
user=> (defn foo [arg1 & args] (apply println arg1 args))
#'user/foo
user=> (apply foo "one" 2 "three" [4 "five" 6.0])
one 2 three 4 five 6.0
To further demonstrate, these calls to + are functionally equivalent:
(apply + 1 2 [3])
(apply + 1 [2 3])
(apply + [1 2 3])
It's also impossible when what you need to do is recur
recur is a special form, and apply doesn't work with it like it would a typical Clojure function.
is there any way at all to accomplish it in the recur case?
Not with apply. You can recur with variadic args, but you can't (apply recur ...).
Related
Here is my code:
(def partial-join (partial (clojure.string/join ",")))
=>(clojure.string/join "," ["foo" "bar"])
"foo,bar"
=> (partial-join ["foo" "bar"])
And it raises this exception:
ClassCastException java.lang.String cannot be cast to clojure.lang.IFn .repl/eval12557 (form-init2162333644921704923.clj:1)
See the doc of clojure.string/join.
clojure.string/join
([coll] [separator coll])
Returns a string of all elements in coll, as returned by (seq coll),
separated by an optional separator.
when only one argument is provided for clojure.string/join, this function regard its argument as collection, so:
user=> (clojure.string/join ",")
","
Next, see the doc of partial.
clojure.core/partial
([f] [f arg1] [f arg1 arg2] [f arg1 arg2 arg3] [f arg1 arg2 arg3 & more])
Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args.
When only one argument provided, partial returns its argument.
user=> (partial (clojure.string/join ","))
","
Try this:
user=> (def partial-join (partial clojure.string/join ","))
#'user/partial-join
user=> (partial-join ["a" "b"])
"a,b"
The problem is NOT with the number of parameters passed to clojure.string/join. The problem is the brackets surrounding clojure.string/join call the function and the result of the function call is passed to partial. What you want to do is pass the function and the first param to partial as below:
(def partial-join (partial clojure.string/join ","))
(partial-join ["foo" "bar"])
;; => "foo,bar"
I understand how apply works in a simple expression like this:
(apply + '(1 2 3))
I have come across a more complex example in a book I am reading.
(def make
(fn [class & args]
(let [seeded {:__class_symbol__ (:__own_symbol__ class)}
constructor (:add-instance-values (:__instance_methods__ class))]
(apply constructor seeded args))))
In the above example, seeded is a map and args is an ArraySeq.
Can anyone explain how apply works in this context?
In this case (apply constructor seeded args) is equivalent to calling (constructor seeded arg0 arg1 arg2 ...). It unwraps the last argument (which must be seqable) and appends them one by one to the list before evaluation.
For example, this: (apply + 1 [2 3]) unrolls to (+ 1 2 3).
It seems to be analogous to:
((make) MyClass) is equivalent to new MyClass()
((make) MyClass "foo" "bar" 3) is equivalent to new MyClass("foo", "bar", 3)
In clojure, I would like to apply a function to all the elements of a sequence and return a map with the results where the keys are the elements of the sequence and the values are the elements of the mapped sequence.
I have written the following function function. But I am wondering why such a function is not part of clojure. Maybe it's not idiomatic?
(defn map-to-object[f lst]
(zipmap lst (map f lst)))
(map-to-object #(+ 2 %) [1 2 3]) => {1 3, 2 4, 3 5}
Your function is perfectly idiomatic.
For a fn to be part of core, I think it has to be useful to most people. What is part of the core language and what is not is quite debatable. Just think about the amount of StringUtils classes that you can find in Java.
My comments were going to get too long winded, so...
Nothing wrong with your code whatsoever.
You might also see (into {} (map (juxt identity f) coll))
One common reason for doing this is to cache the results of a function over some inputs.
There are other use-cases for what you have done, e.g. when a hash-map is specifically needed.
If and only if #3 happens to be your use case, then memoize does this for you.
If the function is f, and the resultant map is m then (f x) and (m x) have the same value in the domain. However, the values of (m x) have been precalculated, in other words, memoized.
Indeed memoize does exactly the same thing behind the scene, it just doesn't give direct access to the map. Here's a tiny modification to the source of memoize to see this.
(defn my-memoize
"Exactly the same as memoize but the cache memory atom must
be supplied as an argument."
[f mem]
(fn [& args]
(if-let [e (find #mem args)]
(val e)
(let [ret (apply f args)]
(swap! mem assoc args ret)
ret))))
Now, to demonstrate
(defn my-map-to-coll [f coll]
(let [m (atom {})
g (my-memoize f m)]
(doseq [x coll] (g x))
#m))
And, as in your example
(my-map-to-coll #(+ 2 %) [1 2 3])
;=> {(3) 5, (2) 4, (1) 3}
But note that the argument(s) are enclosed in a sequence as memoize handles multiple arity functions as well.
Say I have a function that needs two arguments and where the order of arguments affects the results.
Is it possible to pass the first argument into a partial or comp function and the other aside of it, like this:
(defn bar [arg1 arg2] (- arg1 arg2))
(def baz (partial (bar arg1)))
(def qux (comp str (bar arg1)))
(baz arg2)
(qux arg2)
If I want to pass arg2 into the function, could I do something like this?
(def baz2 (partial (bar _ arg2)))
(def qux2 (comp str (bar _ arg2)))
(baz arg1)
(qux arg1)
partial only "fills in" arguments on the left side, so if you need to skip arguments you must use fn:
(def baz2 #(bar %1 arg2))
Note also that comp requires that all its arguments be functions, so your qux and qux2 are actually nonsense. They should be:
(def qux (comp str baz))
(def qux2 (comp str baz2))
In general, Clojure core functions will place the variable most likely to change last to make composition with comp and partial more natural. (E.g., the collection argument is almost always last in Clojure, except for things like into where it makes sense to put it first.) When you design your own functions you should stick to this convention so you can compose your functions more easily.
Scheme SRFI 26 has a useful macro cut for slotting parameters like this.
Usage would be like so for your subtracting bar:
((cut bar <> 1) 2)
;=> 1
((comp str (cut - <> 1)) 2)
;=> "1"
Where the <> symbol represents the slot to be filled.
It is a fun exercise to implement a cut in Clojure yourself, but here is one port by #Baishampayan Ghose.
Here's what I got in a repl:
user=> (defn bar [arg1 arg2] (- arg1 arg2))
#'user/bar
user=> (def baz (partial bar 5))
#'user/baz
user=> (def qux (comp str baz))
#'user/qux
user=> (baz 2)
3
user=> (qux 2)
"3"
It's the closest I could get from your first example.
For the second example, maybe a simple defn is better than a partial def:
user=> (defn baz2 [x] (bar x 5))
#'user/baz2
user=> (def qux2 (comp str baz2))
#'user/qux2
user=> (baz2 2)
-3
user=> (qux2 2)
"-3"
I'd suggest you start a repl and try it yourself, it's the second best way to discover a language that I know of (TDD being the first).
Cheers!
I want to create a function that takes in a required argument x, and either a optional argument opt1 OR a keyword argument opt2.
Right now I have
(defn foo x & [opt1 {:keys [opt2]}]
...
But the above signature only lets me pass in keyword argument opt2 when both x and opt1 is present like
(foo 'x 'opt1 {:opt2 'opt2})
not like this
(foo 'x {:opt2 'opt2})
Please help me create a function that takes a required argument X and either opt1 or opt2, where opt2 is a keyword argument.
Thank you.
EDIT: I want to do the same for other macros as well. So I still need to use the defmacro.
The problem is ambiguity. Consider a function (fn foo [x y & args]) that takes two optional arguments and then any number of keyword arguments. If you then call it like (foo :bar :baz), how does your program handle it? x => :bar, y => :baz? Or x and y not provided, with a single :bar => :baz keyword argument?
Even in Common Lisp, which has arguably even more flexibility than Clojure in parsing function parameters, mixing optional and keyword arguments is not recommended, according to at least one popular book.
Your best bet is to change all of your arguments to positional arguments, or all of your parameters to keyword arguments. If you use keyword arguments, you can use hash-map destructuring to provide defaults for "optional" keyword parameters.
user> (defn foo [& {:keys [x y bar]
:or {x 1 y 2 bar 3}}]
(prn [x y bar]))
#'user/foo
user> (foo)
[1 2 3]
nil
user> (foo :bar :baz)
[1 2 :baz]
nil
you have to check if the aditional arguments are keyword arguments or not anyway (I assume your or is an exclusive or) so you can do it like this:
(defn foo [& args]
(if (= (count args) 1)
(let [[opt1] args] (println opt1))
(let [{:keys [opt2]} args] (println opt2))))
check the arguments if they are keyword arguments or not. As you only have one optional parameter it's easy: check if there's only one as keyword arguments require two.