I am trying to write a container function that takes a function name, it's arguments, and calls the function with an additional argument.
I am pretty sure I am doing this wrong, but here's what I've got so far.
(defn get-dyn [& args]
((first args) "some" (rest args)))
So, the first argument is the function name, and that function can have any number of arguments so I guess I have to accept optional parameter?
I need to inject a common argument "some" into all the functions I call via this function.
so,
(get-dyn str 3 4)
(get-dyn str 32 "willhelm")
etc.. and I would expect it to be equivalent of doing
(str "some" 3 4)
(str "some" 32 "wilhelm")
Except, the (rest args) returns a set so it doesn't work that way.
I tried doing it with partial but couldn't work it out.
Is there a way I can solve this problem? or am I better of using induvidual functions?
You can use apply.
user> (defn get-dyn [f & args]
(apply f "some" args))
#'user/get-dyn
user> (get-dyn str 1 2)
"some12"
user> (get-dyn str " " 1 " " 2)
"some 1 2"
Related
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 ...).
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)
I am doing the closure tutorial at http://clojurescriptkoans.com and I am stuck here: http://clojurescriptkoans.com/#functions/9
It looks like this
Higher-order functions take function arguments
(= 25 ( _ (fn [n] (* n n))))
I am supposed to fill in something at the underscore to make the expression true. I have no clue what to do.
The syntax simply consists of binding the function, and then calling it.
Since this is an exercise, I will show a similar situation rather than showing the exercise's solution:
user> ((fn [f] (f "abc")) (fn [s] (str s s s)))
"abcabcabc"
here I bind the argument of the first function to f, and call f with the argument "abc".
or you can use the short-hand notation:
#(%1 5)
Higher order functions takes functions as arguments.
Defining two functions
user=> (defn multiply [n] (* n n))
#'user/multiply
user=> (defn add [n] (+ n n))
#'user/add
Defining higher order function
user=> (defn highorderfn [fn number] (fn number))
#'user/highorderfn
Calling the higher order function
user=> (highorderfn multiply 5)
25
user=> (highorderfn add 5)
10
I am following along 'Clojure in Action' and I am confused by this :
(defn with-log [function-to-call log-statement ]
(fn [& args]
(println log-statement)
(apply function-to-call args)))
This is the segment of code that has me confused. This is what I can decipher so far:
(defn with-log [function-to-call log-statement ] ..) is defining a function with name "with-log" that takes arguments 'function-to-call' and 'log-statement' and function-to-call is a function being passed as a parameter to this function.
The next section is confusing to me : (fn [& args] .... is an anonymous function being defined here ? Is the 'with-log' function returning a new function definition ?
(fn [& args]
(println log-statement)
(apply function-to-call args))
So by calling (with-log somefunc "my label") -- is it just returing a new anonymous function ? Or is it invoking the anonymous function ?
with-log will yield a function that, when called, will do exactly what function-to-call did except with the side-effect that log-statement will be printed to *out* just before function-to-call is evaluated using the arguments given to the anonymous function.
This is an example of the Decorator Pattern - extending the behaviour of an existing function by wrapping it in another function i.e. the anonymous function created by with-log using the (fn ...) form.
In order for the decorator function with-log to work with any conceivable function-to-call, the anonymous function's argument list is specified so that it can be called with number of arguments using (fn [& args] ...). When the anonymous function calls function-to-call it 'unwraps' the argument list with the function apply).
Ways to make use of with-log might be:
((with-log some-fn "Calling some-fn") arg1 arg2)
or
(defn my-fn [a b]
(+ a b))
(def my-fn-with-logging (with-log my-fn "Calling my-fn"))
(my-fn 1 2) ; evaluates to 3
(my-fn-with-logging 1 2) ; prints "Calling my-fn" and evaluates to 3
It is returning the anonymous function, and it is not being called.
For example, this would invoke the anonymous function with the given arguments:
((with-log some-fn "log statement") arg1 arg2)
This works because the function being returned is the first item in the list, which means it gets invoked just like any other function would.
Yes, you're right. (fn ..) is a form which creates anonymous function. This piece of code, given a function f and some value s will return a function, which, when called, will print s and then invoke f:
user=> (defn with-log [function-to-call log-statement ]
(fn [& args]
(println log-statement)
(apply function-to-call args)))
#'user/with-log
user=> (with-log + "String")
#<user$with_log$fn__1 user$with_log$fn__1#147264b1>
user=> ((with-log + "String") 1 2 3)
String
6
user=>
Note the line starting with #<user$.... This is internal identifier of the anonymous function that just has been created, that is, simple call to with-log returns a function. And then we apply the same function (it is same in terms of its behavior; it will be different object in fact, because each call to with-log creates new "instance" of the same function) to a number of arguments. "String" string gets printed and then REPL shows us a result of (+ 1 2 3).
Here you can learn more about it.