This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # announcements (1)
- # aws (140)
- # beginners (41)
- # calva (47)
- # cider (43)
- # clj-kondo (36)
- # clojure (178)
- # clojure-europe (12)
- # clojure-gamedev (2)
- # clojure-italy (1)
- # clojure-nl (17)
- # clojure-russia (3)
- # clojure-spec (37)
- # clojure-uk (97)
- # clojurescript (173)
- # core-async (16)
- # crux (9)
- # cursive (18)
- # data-science (2)
- # datascript (6)
- # datomic (32)
- # dirac (16)
- # duct (16)
- # events (2)
- # figwheel-main (7)
- # fulcro (8)
- # graalvm (18)
- # immutant (3)
- # joker (2)
- # kaocha (8)
- # nrepl (6)
- # nyc (2)
- # off-topic (62)
- # quil (3)
- # re-frame (18)
- # reitit (6)
- # ring-swagger (1)
- # shadow-cljs (119)
- # spacemacs (4)
- # specter (2)
- # tools-deps (10)
- # vim (58)
Hi folks. I have a noob question about recursion. There's something I'm not seeing and wonder if someone can help me spot it. An example - I'm watching a tutorial where someone is re-writing the filter function. I understand the logic, but I'm struggling to see where in the function we are returning the built up collection. I see that we are using
cons to construct a seq, but I don't see where we are explicitly saying "ok, that seq has been built, we're done building it, no other values to go through, so lets return it." In fact, I don't see where the actual collection is that is being built. Can someone help me see what I'm sure is obvious to the rest of you?
(defn filter* [p? ls] (if (empty? ls) () (if (p? (first ls)) (cons (first ls) (filter* p? (rest ls))) (recur p? (rest ls)))))
@ssanders There are three "arms" to the function: the first
empty?) returns an empty list, the second
if has two arms, both of which involve a recursion on
The two recursive calls either prepend the first element to filtering the rest of the list, or just return the result of filtering the rest of the list.
So if you had
even? as the predicate and
[1 2 3] as the list, you'll get a recursive call with
[2 3] (third arm), then a recursive call with
 to which we'll prepend
2 (second arm), and finally a recursive call with
 (third arm) which will hit the bottom and return
() and as we unwind the calls, we prepend
2 and finally unwind to the top and have
(2) as our result.
Hmmm. I guess I'm not following the "unwind the calls" bit. In your explanation, you said that the function "hit the bottom" and returned
() If it returned an empty list how do we get a list with
(filter* even? [1 2 3]) =>
(recur even? [2 3]) =>
(cons 2 (filter* even? )) and that becomes
(cons 2 (recur even? )) =>
(cons 2 ())
The substitution model as discussed here: http://www.sicpdistilled.com/section/1.1.5/ is a useful tool for understanding how expressions evaluate. http://www.sicpdistilled.com/section/1.2.1/ has examples applying it to recursive processes
I think I'm starting to get it. So you're just building up calls to
cons right? Then once it 'hits the bottom' case, it goes through all of those calls and gives the result?
The way most programming languages are implemented using a stack means calls like cons are sort of suspended until their arguments are evaluated
So the build up of cons you see in the substitution model maps to a build up of stack frames(which are the sort of suspended bits) in a real implementation
There is a way to rewrite code that makes this all very explicit, called continuation passing style, which can help as long as you already understand continuations, but understanding continuations can be tricky and a lot to bite off
Ok. I think I see. The thing I wasn't noticing before was that build up. So calling
(filter* even? (range 5)) Is actually returning the result of
(cons 0(cons 2 (cons 4 ())))
I should have seen that earlier. Making the mental switch from building up a mutable value to building up a function is taking me longer than I'd hoped. Thanks guys
@ssanders The mutable -> immutable shift is probably the hardest part of learning Clojure (or FP in general) for folks coming from most languages.
Taking away assignment and how
for works in most languages (mutating the loop "index") tends to set a lot of folks completely adrift...
Can we justify the use CLJS NodeJS backend vs a Clojure JVM backend? I heard Once that it did not make sense to impose the single thread constraint on the backend and I agree, however it seems many company still like NodeJS.
@neo2551 If you are in a Node.js shop, you'll probably get more support for using ClojureScript on it than trying to bring in Clojure on the JVM.
If you're already a JVM shop, then Clojure makes much more sense on the backend than trying to introduce Node.js to run ClojureScript.
hello, i have a very stupid question, how can i dispatch a route with secretary inside a callback from async? This is my code:
the call is successful but the browser is not redirected to the homepage 😕
(defn successfull-call [response] (:success response)) (defn login-callback [response] (if (successfull-call response) (secretary/dispatch! "/home"))) (defn call-login [data] (let [data-channel (go (<! (http/post login-url data)))] (take! data-channel login-callback)))
Hi, I'm trying to use a class from a java library, I've imported the class, Now the java example says:
Thank you! found another way to write it on stackoverflow using (->) https://stackoverflow.com/questions/8821751/how-do-i-create-a-java-like-object-in-clojure-that-uses-builder-pattern
Is there a standard way to collect a collection by increments? Like
[a b c d e f g h] to
[ab cd ef gh]?
I’m using spacemacs, is there a way when editing the
project.clj and adding dependencies to search for and update the dependency version or do I need to search github and find the latest release tag?
I use CIDER, refactor-nrepl, and clj-refactor: https://github.com/clojure-emacs/clj-refactor.el See https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-add-project-dependency