This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # bangalore-clj (1)
- # beginners (67)
- # braveandtrue (179)
- # cider (28)
- # cljdoc (1)
- # clojure (132)
- # clojure-conj (3)
- # clojure-dev (1)
- # clojure-finland (6)
- # clojure-nl (2)
- # clojure-russia (6)
- # clojure-spec (19)
- # clojure-uk (62)
- # clojurescript (90)
- # clojutre (5)
- # component (2)
- # cursive (30)
- # data-science (1)
- # datomic (42)
- # duct (9)
- # emacs (1)
- # figwheel-main (158)
- # fulcro (57)
- # funcool (3)
- # hoplon (1)
- # jobs (17)
- # mount (38)
- # off-topic (15)
- # re-frame (53)
- # remote-jobs (2)
- # schema (11)
- # shadow-cljs (299)
- # spacemacs (25)
- # specter (2)
- # tools-deps (54)
- # vim (11)
- # yada (6)
I assume I need to use loop/recur or reduce, but I can’t figure out how to properly handle the arguments
Actually not, in this case. There's an idiom that shows up fairly commonly in Clojure code (and in functional code in general) that uses multiple arities to break an arbitrary number of arguments down into something manageable, using recursion.
Arity, in case you're not familiar, is just a reference to the number of arguments a function accepts. Here's a multiple-arity Clojure function that takes either 1 or 2 arguments:
(defn foo ([x] (foo x 0)) ([x y] (+ x y)))
In this case, the 1-arity version of the function calls the 2-arity version, so it's going from a smaller number of arguments to a larger number. But you can go the other way as well, and have a bigger-arity function call a smaller-arity function.
I'm not going to give you any more hints than that because it's probably more useful for you to wrestle with this a bit. Well, just one more hint: Here's a function that takes any number of arguments:
(defn foo2 [x y & z] ;; do something with x y and z )
If you call
(foo2 :a :b), then inside the body of
x will be
y will be
z will be
That should be enough to let you build your own
comp function that takes any number of arguments, but you'll need to think functionally/recursively to figure it out.
(I'll be honest, I think I would have wrestled with this one for a while when I was first learning FP...)
what is tricking me, is that I’m sure I shouldn’t be checking the type. But I can’t figure out how to separate an arbitrary number of 1st class functions from their parameters.
Ah, there’s a catch to
comp that will simplify things greatly. Only the last (rightmost) fn can take 0-n arguments. All the other fns must take only a single argument, which will be the result returned by the fn on the right.
my-comp functions, the only arguments
my-comp will need to worry about are the
fn3 etc args. not the
all the other args
oh I won’d need to worry about the args because this works:
(def my-plus +) (my-plus 4 5)
so the arguments to that list of functions will be applied later, after you get done composing them.
Ok, look at how the built-in comp fn works:
(def double-inc (comp inc inc)) (double-inc 1) ;; ==> 3
Notice we’re using
defn, and we’re passing two bare functions to
comp (they happen to be the same function, but that’s ok)
Ok cool. So all
defn is really doing is just giving you some syntactic sugar for something like this:
(def my-fn (fn [a b] (+ a b)))
(fn [a b] (+ a b)) is an anonymous function that takes 2 args and returns the sum. The
def takes that function, and assignss it to a name so that you can call
(my-fn 1 2)
def in other words, takes a value and assigns a name to it. In clojure, anonymous functions are also values, just like numbers and strings are values. So you can assign them to vars using
defn is just a shortcut that lets you define a function and give it a name all in one step
okay that helps, what is different between:
(def my-plus +) ;; which works for any number of parameters and (defn my-plus [a b] ; gotta put something here in [ ] (+ a b)) ; but this only works for two params
+ symbol right now is a name that points to a function that does addition. If you type
+ by itself you’ll get the value of
+, which is an anonymous function. Since it’s a value, you can assign it to other vars as well, which is why
(def my-add +) works.
the part I don’t understand, is how to define a
defn because then I imagine I’ll need to deal with parameters
and the right answer doesn’t feel like it involves tailing off the last parameter with
So probably the part you’re missing is how to use multi-arity functions to do the same sort of thing as loop/recur
Here’s the (simplified) source code for the
(defn + "Returns the sum of nums. (+) returns 0....'" ;; metadata stuff, skipped ( 0) ([x] (cast Number x)) ([x y] (. clojure.lang.Numbers (add x y))) ([x y & more] (reduce1 + (+ x y) more)))
So we’ve got a zero-arity version, a single-arity version, a 2-arity version, and a more-than-2-arity version.
The trick here is that this reduce is actually happening inside the definition for +
for clarity is this equiv?
(defn my-plus "Returns the sum of nums. (+) returns 0....'" ;; metadata stuff, skipped ( 0) ([x] (cast Number x)) ([x y] (. clojure.lang.Numbers (add x y))) ([x y & more] (reduce1 my-plus (my-plus x y) more)))
Checking out reduce1… https://github.com/clojure/clojure/blob/clojure-1.7.0/src/clj/clojure/core.clj#L895
(reduce1 my-plus (my-plus x y) ; combine two elements into one by calling the 2-arity) more))) ; now more is one shorter?
You got the right idea, but I wouldn’t write it like that, I’d just say x=1, y=2 and more=[3, 4]
Right. Notice that in the middle, the
(+ 1 2) is a call to the 2-arity version. That’s the recursion trick--you know how to handle 2 args, so you use that arity to reduce 2 args down to 1, and now your list of arguments is one shorter.
You went an extra step or two — once the list of arguments is down to 2, you just hit the 2-arg version and don’t need to keep recursing any more.
The arguments to
+ will be the numbers without the vector — yes, the
& is what makes
more a vector
never seen anything unusual, give it a function and it just calls it. It does pass in the on-click event, so maybe add that in as an arg to your handler function?
Let me see if I can give you one more nudge in the right direction by writing out a verbal description of what
+ is supposed to do. Actually, let me call it
sum, it will read easier in english:
So the recursive thinking is in the last case where we’ve defined the
(sum) with zero arguments should return 0 (sum n) with 1 arg should return the arg (sum n1 n2) with 2 args should return the result of adding them <== the ending case, does not recurse! (sum n1 n2 n3...) with more than 2 args should return the sum of (the sum of the first 2 args) and the remaining numbers
sumof a big list of numbers in terms of finding the
sumof 2 smaller lists of numbers. Since the list gets smaller each time we recurse, we know we’ll eventually reach the end case of only 2 arguments.
but, let’s take something non-recursive to show where I’m stuck, a comp function that only takes two functions
like, do I need to build a tail-recursion part to handle inputs to the final function or am I missing something else
Ok, there’s one more piece you’re going to need, and that’s the opposite of
Actually, we’ve reached the point where the problem you’re working on is making me have to stop and think
You’re on the right track, because you definitely need the right-most function to be the first one that actually executes
Ok, I’d suggest tackling a simpler version first. Write
my-comp assuming that it will only ever be given functions that take exactly 1 argument.
(defn my-2comp [f1 f2 & more] (f1 (f2 more))) is wrong because 2-comp should only take two functions
The other thing you’re missing is that you’re writing
my-2comp such that it takes 2 functions and returns the result of calling those functions, which is wrong
my-1comp is returning a function, which is correct. It’s not calling the function, it’s just returning it
I see that’s why this happens:
(defn my-1comp [f] (f)) (def test-fn (my-1comp -)) ; --> CompilerException clojure.lang.ArityException: Wrong number of args (0) passed to: core/-, compiling
my-1comp (first version) works because you only have one argument, so you can just return it.
Right, and the way you do that is by returning an anonymous function
(fn [...] ...)
You could put it that way (but “lazy” usually refers to something slightly different in Clojure)
that’s how I return a 1st class function instead of returning the immediate result of applying that fn
Correct (which is why I recommend starting off with only comp-ing single-arity functions)
Once you have a
comp that can compose any number of single-arity functions, you can take that solution and adapt it to return a multi-arity anonymous function.
I think i have enough to puzzle it out. There is the composition and the arguments and the returning of a fn. So that’s 3 parts.
apply works and does the wrapping. I suppose I ’ought to implement
apply after this to make sure I get that part too.
Looks good except the zero-arity version of your anonymous function is wrong. Since you’re inside the anonymous function, you do need to call
f. Your 1-arity and more-than-1-arity versions are calling
f correctly, you just need to have the zero-arity version call it too (with no args, of course, since it’s zero-arity)
my-1comp function is doing the right thing by returning an anonymous function instead of having
my-1comp try to call
(f) was wrong before was because in the earlier versions it wasn’t inside a
(fn [...] ...)
my-1comp is returning an anonymous function, you need to have the anonymous function actually call it.
I tested with with
(def myprintln (my-1comp println) with the
f version calling
(my-println) returns an object ref to the fn, whereas
(f) does what it should