This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-12-20
Channels
- # adventofcode (47)
- # announcements (3)
- # aws (29)
- # bangalore-clj (3)
- # beginners (63)
- # boot (2)
- # braveandtrue (40)
- # calva (34)
- # cider (37)
- # cljs-dev (8)
- # clojars (3)
- # clojure (45)
- # clojure-europe (2)
- # clojure-france (4)
- # clojure-india (2)
- # clojure-italy (44)
- # clojure-japan (4)
- # clojure-nl (39)
- # clojure-serbia (1)
- # clojure-spec (21)
- # clojure-uk (75)
- # clojurescript (28)
- # cursive (24)
- # data-science (3)
- # datomic (31)
- # emacs (13)
- # fulcro (35)
- # hoplon (21)
- # jobs-discuss (66)
- # nrepl (18)
- # off-topic (72)
- # pathom (35)
- # re-frame (20)
- # reagent (54)
- # shadow-cljs (35)
- # spacemacs (9)
- # specter (8)
- # sql (13)
- # testing (9)
- # tools-deps (21)
- # vim (3)
Sorry for answering a day later, but our time zones are probably way off and I had a busy day. š
No problem, Slack is actually not too bad at compensating for timezone offsets, if you donāt mind waiting a bit for replies. š
I played with writing a my-comp
function, and I used recursion (not reduce
). I will say that Iāve been writing Clojure for 7 or 8 years, and Iām pretty comfortable in it, but I still found this problem a bit tricky, even having a pretty good idea how it should be solved.
The basic pattern of recursion is that you figure out the easy problem, also known as the ābase caseā, and then figure out how to have the function solve slightly more complex problems by calling itself. And itās only slightly more complex. You donāt have to figure out all possible answers, you just have to figure out two cases: the base case, and the ābase case + 1".
So, for example, to write a factorial function, you first solve the simple case where n = 1
(defn fact [n]
(if (= n 1)
1
,,,
))
Then for numbers greater than 1, you take the definition of āfactorialā and try to find some way to phrase it in terms of itself.
factorial(n) = n * (n - 1) * (n - 2) * ... 2 * 1
is the ānaiveā definition of factorial, but we can write that more concisely by defining it recursively: factorial(n) = n * factorial(n - 1)
So in Clojure terms we get this: (defn fact [n]
(if (= n 1)
1
(* n (fact (dec n)))))
So far so good? This is the āeasyā part ā weāre doing recursion with data, and getting back a result that is data. What makes Chapter 5 tricky is that we want to do the same thing with HOFs (Higher Order Functions).
We still want to solve the problem the same way: look for a simple, base case that we know the answer to, and then try to find a way to define our fn recursively so that it eventually winds down to our simple base case.
For this particular problem, Brave and True gives us the simple base case: ācompā with two functions.
(defn brave-comp [f g]
(fn [& args]
(f (apply g args))))
Now all we have to do is figure out the recursive case.Iāll stop there so you can have a look at this and ask any questions. This might be enough to get you heading in the right direction, but recursion is a tricky concept to master, and doubly tricky when youāre trying to apply recursion to higher-order functions. Weāre pretty close to solving this though, so you might want to take a whack at this on your own and see if you can crack it.
Awesome explanation! Thank you š I have tried many hours yesterday after I spoke here, and have a file full of unfinished half-thoughts, but with this explanation I think that I have at least a clue. Well, it's not working right now, but I believe I'm not that far, so I'll keep trying a bit more š
I really don't get the part of "a way of using recursion that will naturally give you the last-to-first order without reverse" š
Well, I'd say just ignore that part for now and just see if you can figure out the recursive bit.
hehehe
If you get the recursive bit, the last-to-first order bit will kind of solve itself.
This is what I have so far
(defn my-comp [f & fs]
(fn [& args]
(let [res (apply f args)]
(loop [[head & tail] fs
result res]
(if (not (nil? tail))
(recur tail (apply head (vector result))))))))
But it isn't working š gives me "nil" always.I have no experience with forcing evaluation with apply, so maybe this is where it's going wrong.
And using reverse
I could probably just ask for [& fs]
in my-comp
and reverse it, extract the head, etc.
But without it, I have no idea.
My solution does not use loop/recur at all.
It does have the base case, though.
The structure of my solution looks very similar to the factorial
function I used as an example.
I see, let me try a bit more then š
Good luck. š
Though you can use recur without a loop. It makes recursion a bit safer in clojure but youāre probably not recursing a lot with a comp function though š
You only need to recurse once, and you donāt need recur
to do it. š
2 or more functions (you could do it so that it supports 1 function, but thatās kind of an edge case and it nicer not to worry about that until you get the 2-or-more version working)
If you look at my factorial
function above, the function definition contains 1 call back to itself, without loop
or recur
.
Oh right, I think I used the wrong term. It still iterates n times. Just as your comp will have to call itself for however many functions are provided to it?
Right, it will keep recursing until it gets down to the base case, which is the 2-argument version of my-comp
Ah ok, weāre on the same page then. Isnāt it preferred to use recur though rather than directly calling the function?
Depends on your goal: if you want to avoid blowing the stack for long sequences, recur
is better. If you want to illustrate the principle of recursion, itās nice not to have to worry about it, and just call directly--it makes the code a little simpler.
Arguably, comp
is a special case: youāll only be using it for code that you write, and youāre unlikely to try and comp together so many functions that you blow the stack. (It could happen, though, in which case youāll need the recur
version.)
In general real-world programming, itās usually better to use recur
to avoid the stack overflow problem.
Hmm, āDepends on your goal..ā etc sounds snooty. I should have just said, āYes, usually you would prefer recur
and Iām just making an exception in this case to make the exercise simpler.ā
Youāre right. The code is simpler doing it directly the solution I had in mind would only have recurād the inner anonymous function when we need to reach the outer my-comp function.