This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-12-14
Channels
- # adventofcode (38)
- # announcements (42)
- # aws (3)
- # babashka (8)
- # beginners (165)
- # calva (36)
- # chlorine-clover (14)
- # cider (6)
- # clj-kondo (5)
- # cljsrn (33)
- # clojure (27)
- # clojure-australia (1)
- # clojure-czech (1)
- # clojure-doc (1)
- # clojure-europe (26)
- # clojure-nl (6)
- # clojure-spec (6)
- # clojure-uk (3)
- # clojurescript (10)
- # code-reviews (20)
- # conjure (1)
- # core-logic (5)
- # cursive (3)
- # data-science (5)
- # datomic (35)
- # emacs (1)
- # figwheel-main (3)
- # fulcro (10)
- # honeysql (1)
- # introduce-yourself (4)
- # jobs (3)
- # jobs-discuss (4)
- # minecraft (2)
- # missionary (28)
- # nextjournal (3)
- # off-topic (45)
- # pathom (7)
- # polylith (1)
- # portal (22)
- # practicalli (2)
- # re-frame (4)
- # reagent (19)
- # releases (3)
- # remote-jobs (3)
- # reveal (1)
- # rum (4)
- # shadow-cljs (37)
- # spacemacs (14)
- # sql (1)
- # tools-build (7)
- # tools-deps (16)
- # vim (13)
- # xtdb (15)
I'm sure that works, I try to avoid editor / tool specific suggestions
I have a vector like this [5 7 "missing" 10 "missing" 11] and I have to calculate something like this [0 (7-5) "missing" (10-7) "missing" (11-10)] seems very complicated to me how can I do that ?
I'm not totally sure that I understand what you're trying to get out of it, but you could start with something like
(->> [5 7 "missing" 10 "missing" 11]
(partition-by string?)
(partition-all 3 1))
where we use partition-by string?
to group the ranges of numbers separately from the "missing"
values and partition-all
to get a window on the collection, so you can extract the high and low values for the range? ... Does that make sense?it is like difference between consecutive values but we have missing values in between
It seems like a strange requirement (e.g. you can’t just filter out the missing values?) but assuming that the requirements are sound then you need some means of processing each element while also being able to see across the remainder of the input vector.
One way to do that is with map-indexed
. By having the index, you can look across vector up to that offset for the last thing to subtract from.
Another way is with reduce
where you accumulate your result, and keep track of the last non-missing value you saw as you process the list.
The first thing that came to mind for me was to use a simple loop
. Work backwards through the vector, and each value is either itself (when missing) or itself minus the first non-missing value in the remaining list.
(defn missing? [x] (= "missing" x))
(defn subtract
"return 0 if the second number is nil"
[a b] (if b (- a b) 0))
(loop [[n & r] (reverse v) result ()]
(if-not n
result
(recur r (cons
(if (missing? n)
n
(subtract n (first (remove missing? r))))
result))))
The reduce approach is to accumulate the result with the last non-missing value. Then use first
to just get the result out (and drop the last valid value)
(first
(reduce (fn [[result lastn] n]
(if (missing? n)
[(conj result n) lastn]
[(conj result (subtract n lastn)) n]))
[[] nil] v))
Doing it with map-indexed
doesn’t feel as “right” to me, but:
(map-indexed (fn [i n] (if (missing? n) n (subtract n (last (remove missing? (subvec v 0 i)))))) v)
Obviously, there are more ways to do it 🙂 These are just the first 3 that came to mind. But the general principle is that whatever is processing the sequence needs to have some kind of context of where it is, so it can either look for the last non-missing value, or else remember what that last non-missing value was
I'm thinking about having such aliases in my config
{:aliases {"foo" #{"888832288459194439" "888832288459194443"}}
:m
{:a #{"foo"}}}
"expand"
=>
{:aliases {"foo" #{"888832288459194439" "888832288459194443"}}
:m
{:a #{"888832288459194439" "888832288459194443"}}}
1. do you have a better ideahi, i've been working through advent of code and I find myself using recursion for a lot of the exercises. One thing I'm struggling with is falling into infinite loops. I was wondering if there are any easy tricks or patterns that I can use to avoid this? eventually the repl will break out of the loop but it can be a bit of a pain
If you can translate the problem to linear recursion (with recur) you can use reduce, then you know it will always end If not, could you perhaps model your problem as a postwalk of some data structure?
inside a loop/recur, always first write an if
with your stop condition
you will only fail to stop if a) you don't have a termination condition and b) you are not changing something in every pass through the loop that will eventually affect the termination condition
Though I can say from personal experience that if you get it wrong then you'll still recur into infinity 🙂
but those are the only two reasons why so explicitly think about both of them, both when you write and when you debug
is it generally more idiomatic to lean on reduce where the recursion is linear? at least then i know i'll be stopping
in general, seq/transduce >> reduce >> loop/recur
Always have one binding which accumulates and one which decreases in size. Make sure you always check the one which decreases and that you always decrease it. It can be consuming a list or counting down to zero
stuff on the left is higher-level and often better
it's probably a lack of practise also, i'm still getting comfortable with repl based dev
that's interesting, ben. Is the idea of the two bindings that if they're both the same then I can break out of the loop?
maybe also helpful is to be aggressive about extracting logic out to functions you can test independently so that the logic of the recursion ends up as simple as is reasonable
but I think something you've just said resonates wiht me. I have a recursive loop where I pass in a list and then filter inside the loop. I think if i filtered then recurred, I'd be able to diff the inbound list and break out of the loop:
(defn process-flashes [grid]
(loop [grid grid
flashed #{}]
(let [points (keys grid)
can-flash (remove flashed points)
flashing (find-flashing grid can-flash)
updated-grid (process-flash-points grid flashing)]
(if (empty? flashing)
updated-grid
(recur updated-grid (into flashed flashing))))))
if points
came in as an input param rather than in the let I guess I could see if it's decreased and break out if ithasn't
another possibility is to extract the recursion and termination condition by using iterate
and focussing on the repeated application. this way you can take
or drop
as many results as you like to debug step by step
Usually, your stop condition is when you have consumed all of your input and built all of the result, or if you have some early exit clause
yea i think if i moved points to an input param that could be my termination clause
oh this is a great case for iterate, good call @U02QXKSHP97
@U027433844C i don’t want to spoil day11 for you. i noticed i did solve it using iterate
. if you’re interested in taking a look after you’ve debugged, feel free to ping me, i’ll send you a link
@corasaurus-hex thanks, i keep rediscovering it around AdventOfCode time every year 🙂
quite a lot of good input in this thread. To be honest I didn't expect so much feedback and help
i thought about passing some sort of counter in as a paramter of the recursive call but this feels a bit clunky
One rule of thumb is to construct the base case first. Then prove to yourself that you are going to work towards that base case in your recursion.
hi, I wrote the snake game in clojure using quil: https://github.com/francesco-losciale/snake is there anyone available to review these two code pages? how would you improve this code in terms of readability and conciseness? - note https://github.com/francesco-losciale/snake/blob/502c6794b174bfd15a8c358c2d59b8fd204f8c67/src/snake/grid.clj#L4 I had to use in grid.clj
thanks
in the future this is a good thing to post in #code-reviews
the only real issue I see is the trailing )
on their own line - that hurts readability, we just don't do that
top level def for constant values is normal, it would be an issue if you were mutating them mid run of the program, but you aren't
I see that it's expected for quil to run your app when a namespace is required, in normal clojure this is considered pathological behavior
the norm is that a namespace define a -main
function if applicable, and then something outside it (eg. a repl invocation or an arg to clojure.main
) arranges for it to be called
thanks @U051SS2EU
hi 🙂 In `try … catch … finally` …. the contents of the finally clause has nothing whatsoever to do with the return value. It is intended for side-effect code only. After executing the code inside your finally clause, Clojure goes back to the last position reached in your try or your catch and returns that. This is a little surprising. It’s almost like “penultimately” instead of “finally”
(try (println "in body") (finally (println "in final")))
i get “in body” before “in final”
this returns the nil from the “in body” println instead of the nil from the “in final” println …. not that it matters in this case since they’re both nil
comment was about the return value, not order of execution
this is one place where Clojure's implementation differs from Java - the finally in Java can return and that takes precedence over the body
given the existence of with-
forms for closing off resources, what is the rationale for finally to be different from Java?
I don't actually have a good answer for that but I suspect it has something to do with tail recursion
try/finally basically behaves like http://clhs.lisp.se/Body/s_unwind.htm
@fappy to me it sounds like you are blurring the distinction between values and processes. clojure doesn't return the value of the last form in a body because it was the last thing processed, it returns it because that's the value that do
is defined to return (`let` / doseq
/ fn
/ catch
/ finally
/ when
etc. each have an implicit do
- notably for
does not).
(defn try-finally [try-thunk finally-thunk]
(let [ret (try-thunk)]
(finally-thunk)
ret))
“finally” is a potentially confusing name 🙂 HUGE plug btw for the naming chapter in “Elements of Clojure” by @ztellman: https://leanpub.com/elementsofclojure/read_sample
does clojure happen to have type hints?
^TypeName before a symbol, yep
This question makes me nervous. Clojure has a concept called type hints but it might not be similar to what you have in mind.
i get that
oh, https://clojuredocs.org/clojure.reflect/typename this is absolutely not what i thought it would be 🙂
wait what does typename
have to do with this?
if anyone happens to know how python has type hints, and where IDE nicely tells you this is supposed to be a string when you try to assign int?
that's what i imagined in my head 😅
so you mean an IDE feature, not a language feature
or even better, a language feature, like typescript compiler
clojure doesn't have IDE features per se, you should probably ask in the channel for your IDE?
what would the language feature do?
probably not exactly a feature most clojure developers would seek anyway?
there are lots of tools, cursive (the intellij plugin), https://typedclojure.org/ that might sort of provide that
clj-kondo has some nice type hinting at places
it's not like, say, java or typescript or anything, but it's helpful
add in clojure-lsp and you've got a stew cookin'
(I think clojure-lsp uses clj-kondo under the hood for things?)
this is indeed the type of thing i was intending to ask about, although i probably articulated myself poorly
thanks 🙂
thinking about why I don't look for this feature - my code is usually in one of these contexts: • the data type is sequential collection • the data type is associative collection • I'm at an edge / in the bottleneck working on a specific primitive data type • I'm doing interop with a well defined and documented class from java
also I very rarely find myself using third-party-glue, which is where I'd most likely need these tips
clojure.core is small enough that I can expect I know most of it
that's fair
like - I know what this weird data type is because I wouldn't even be using this library except for the need to use the data type
(or I knew it already because this is core)
clojure.spec might be something to look, which is a way to describe schemas for clojure data, but it is pretty dynamic at runtime stuff
@filip.strajnar to be clear I'm not trying to convince you you don't need the feature, just figuring out loud why I haven't missed it myself
(none built in, I think there have been some projects looking at it, but it is hard to add because of how open ended spec is)
I rather like types in typescript. it feels like a good middle ground between stricter type systems and completely dynamic systems
oh, don't worry i have exactly 0 experience with clojure, so i don't have any reason to assume my previous ways would be relevant at clojure
it'd be unfair of me to say i'm making a good suggestion
or rather, making a good question
you were asking if a concept you are familiar with had an analogue here. that’s fair 🙂 . And I would also love a static type system with row polymorphism. Its not unfair to want that 🙂
for context, I'm a developer using C# (http://ASP.NET core), so I'm simply asking around for familiar concepts from this language
clj-kondo offers a basic form of type checking.
(+ 1 :foo)
^--- Expected: number, received: keyword.
if there's a continuum, clojure is definitely pretty far to the "not specifiying types" extreme, I've found it normal to need to read definitions down two or three deep before I understand what data types are being used when looking at other people's code
I'd image so
but there's a silver lining that very few types are used in practice
I was mostly attracted/curious about clojure because of macros
Probably worth mentioning that macros are not very widely used in real-world code in general. We try to do as much as possible with regular functions and use macros to provide "syntactic sugar" over function calls. A few things require macros -- those involving deferring evaluation, for example, such as when
(which is a macro that expands to an if
expression).
Macros don't compose like functions so they aren't a good "API" in general but they can be useful to provide a sort of DSL for humans (often implemented on top of a programmatic API that does compose).
While it's true that some programs and systems don't make much use of macros, they are 1) a critically important feature of clojure and lisps and in general 2) worth learning! Macros provide capabilities that aren't always needed, but really save the day when they are. Macros also provide a playground for learning concepts that are otherwise less accessible. I agree that it's a nightmare if you need to work on a code base that uses macros unnecessarily and gratuitously, but if you're interested in macros, I really do encourage you to try them out!
Right, agreed, but when I see someone attracted to Clojure "because of macros", I always feel it's worth tempering that with "Hey, wait a minute there... that's not the real reason to use Clojure!" 🙂
When I see someone excited about learning macros, it reminds me of why I initially decided to learn clojure and how much fun it was. If that's what someone is excited about, I say "go for it!"
Hahaha... okay, fair enough. This old curmudgeon has had a rough several days so I'm even more grumpy than usual! get off my lawn!
If I were to be attracted to a language because of macros, I’d go to Racket. Indeed, Racket is on my list of languages to learn specifically because of its famous macros
macros are very foreign in C#, so it sounds exciting
@borkdude that too - the observability and reflection make a big difference
as for recommended recourses, is there a book or some articles i should read on?
or should the clojure website tutirial suffice
Here is a list that might be useful: https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f You can also do exercises here: https://4clojure.oxal.org/
outside the box a little: IMHO most of clojure's best ideas come from ml and scheme, and spending a weekend on each of those will leave you well prepared to write good clojure code
surprisingly large amount of resources to be honest
(and both are excellent languages for learning about languages)
thank you all so much for all the information
appreciated
there are so many resource on there
may I also ask about which IDE/Text editors you use?
@corasaurus-hex I see, the list borkdude sent was great too
i didn't expect this many resources for this language
Probably worth mentioning that macros are not very widely used in real-world code in general. We try to do as much as possible with regular functions and use macros to provide "syntactic sugar" over function calls. A few things require macros -- those involving deferring evaluation, for example, such as when
(which is a macro that expands to an if
expression).
easiest to get started with, right now, is probably calva (if you don't already know emacs or intellij)
that's totally my opinion, of course, and others here may disagree
If you already know VS Code, I'd agree with the suggestion to use Calva for Clojure @filip.strajnar
I know a bit of vim, no emacs
i've used intellij, but i dislike it
i'm very fond of VS code
indeed 🙂
Join the #calva and #vscode channels to get questions answered!
thanks :thumbsup: