braveandtrue

https://www.braveclojure.com/
felipebarros 2018-12-20T01:47:07.080400Z

I do need help with this, because I can't seem to find a way out of it. Even with your reduce tip.

felipebarros 2018-12-20T01:52:50.081700Z

Sorry for answering a day later, but our time zones are probably way off and I had a busy day. 🙂

felipebarros 2018-12-20T01:54:18.081800Z

I'm curious to see your solution 🙂 what is a pipe function? And its the first time I see butlast, thanks!

felipebarros 2018-12-20T01:55:32.082Z

I mean, I have used | and others in the shell, but in Clojure I'm not sure what you mean by it.

2018-12-20T02:28:58.087200Z

In functional programming a common alternative to comp is pipe where functions are applied top to bottom. It’s very much the same principle with the Unix pipe you mentioned. Hypothetically in Clojure it would be like (pipe :a :b) instead of (comp :b :a). However Clojure has thread macros which are way better.

1💯
felipebarros 2018-12-20T04:58:55.088500Z

Wow, how come I haven't thought of just evaluating one function at a time and building a result? Instead I just assumed I had to return a function that was composed of other functions like loop itself haha nice one 🙂

felipebarros 2018-12-20T05:00:55.088700Z

And I never thought of thread macros as something similar to pipes... since English isn't my main language, I take a lot of time to associate different names to the same concept I guess. But yes, I've learned about Clojure's thread macros a while back and always end up using them. 😄

1👍
2018-12-20T11:52:51.090100Z

No problem, Slack is actually not too bad at compensating for timezone offsets, if you don’t mind waiting a bit for replies. 🙂

2018-12-20T11:56:11.091900Z

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.

2018-12-20T11:59:02.094200Z

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".

2018-12-20T12:01:52.095800Z

So, for example, to write a factorial function, you first solve the simple case where n = 1

(defn fact [n]
  (if (= n 1) 
    1
    ,,,
  ))

2018-12-20T12:07:13.100Z

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)))))

2018-12-20T12:09:48.101500Z

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).

2018-12-20T12:11:52.102900Z

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.

2018-12-20T12:13:48.104700Z

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.

2018-12-20T12:20:39.107200Z

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.

1👏
felipebarros 2018-12-20T19:33:53.108500Z

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 😄

felipebarros 2018-12-20T19:51:02.109300Z

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" 😕

2018-12-20T19:52:08.109800Z

Well, I'd say just ignore that part for now and just see if you can figure out the recursive bit.

felipebarros 2018-12-20T19:52:35.110300Z

hehehe

2018-12-20T19:52:46.110700Z

If you get the recursive bit, the last-to-first order bit will kind of solve itself.

felipebarros 2018-12-20T19:53:03.111100Z

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.

felipebarros 2018-12-20T19:53:49.111700Z

I have no experience with forcing evaluation with apply, so maybe this is where it's going wrong.

felipebarros 2018-12-20T19:56:35.112400Z

And using reverse I could probably just ask for [& fs] in my-comp and reverse it, extract the head, etc.

felipebarros 2018-12-20T19:57:09.112700Z

But without it, I have no idea.

2018-12-20T20:00:31.113300Z

My solution does not use loop/recur at all.

2018-12-20T20:01:00.113600Z

It does have the base case, though.

2018-12-20T20:03:11.114500Z

The structure of my solution looks very similar to the factorial function I used as an example.

felipebarros 2018-12-20T20:05:05.115100Z

I see, let me try a bit more then 🙂

2018-12-20T20:08:34.115800Z

Good luck. 🙂

2018-12-20T21:09:09.117Z

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 😄

2018-12-20T21:11:23.117400Z

You only need to recurse once, and you don’t need recur to do it. 😉

2018-12-20T21:11:58.117700Z

We’re talking about a comp implementation that takes 1 or more functions right?

2018-12-20T21:12:54.118700Z

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)

2018-12-20T21:13:54.119900Z

If you look at my factorial function above, the function definition contains 1 call back to itself, without loop or recur.

2018-12-20T21:15:47.121100Z

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?

2018-12-20T21:16:49.121900Z

Right, it will keep recursing until it gets down to the base case, which is the 2-argument version of my-comp

2018-12-20T21:17:47.122700Z

Ah ok, we’re on the same page then. Isn’t it preferred to use recur though rather than directly calling the function?

2018-12-20T21:19:08.123900Z

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.

2018-12-20T21:20:21.125100Z

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.)

2018-12-20T21:21:46.126200Z

In general real-world programming, it’s usually better to use recur to avoid the stack overflow problem.

2018-12-20T21:23:17.127500Z

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.”

2018-12-20T21:24:28.128800Z

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.

2018-12-20T21:25:20.129700Z

You could probably make that work by adding more args to the anonymous function but likely not practical or worth it

2018-12-20T21:25:27.129900Z

Thanks for explaining.

2018-12-20T21:25:35.130100Z

my pleasure 🙂

1👍