This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-12-19
Channels
- # adventofcode (18)
- # announcements (1)
- # babashka (153)
- # beginners (73)
- # bristol-clojurians (4)
- # calva (1)
- # cider (6)
- # clj-kondo (38)
- # clojure (154)
- # clojure-dev (12)
- # clojure-europe (7)
- # clojure-finland (11)
- # clojure-nl (70)
- # clojure-spec (13)
- # clojure-uk (101)
- # clojuredesign-podcast (2)
- # clojurescript (15)
- # core-async (30)
- # cryogen (1)
- # cursive (5)
- # devops (1)
- # duct (4)
- # figwheel-main (1)
- # fulcro (19)
- # jobs (12)
- # kaocha (17)
- # luminus (2)
- # malli (8)
- # music (5)
- # nrepl (13)
- # off-topic (20)
- # overtone (3)
- # re-frame (7)
- # reagent (38)
- # shadow-cljs (13)
- # specter (3)
- # tools-deps (6)
- # vim (7)
Data structure question. What is the idiomatic way in clojure to have a map where I can do binary search over the keys?
Should I be using: https://github.com/clojure/data.finger-tree?
https://github.com/clojure/data.avl seems to be doing most of what I need.
@finn.volkel I am pretty sure a sorted-map or sorted-set should do all of those things. Implemented using red-black trees under the hood
IIRC, some combination of subseq
and rseq
and other sequence operations should do what you want, although it does not keep "pointers" into the ordered sequence of keys, so starting with a key and then finding smaller/larger ones will probably always be O(log N) for the first, then O(1) for each one afterwards as long as you keep going.
@andy.fingerhut thanks, I wasn't aware of the subseq
and that it runs in O(log n). So you are saying something like (first (rsubseq (sorted-map .....) <= x)))
in only O(log n) even if the subsequence returned by rsubseq
might be quite large?
It is O(log n) to find the first element, then I believe it is O(1) per element that you actually traverse after that. If you traverse m elements, it cannot be faster than O(log n) + linear in m
Clojure rarely provides core functions that do not have good performance characteristics, in general. Very little of the built-in documentation in doc strings gives big-oh notation running time -- a few will mention constant or logarithmic time, but not many.
A "sequence" in Clojure is some abstract thing that promises to implement first and rest. Many of them are implemented lazily, so do not realize long things unless you traverse them.
@finn.volkel ^^^ not sure if you saw that, so pinging you via @ in case not. Sorry for the bother if you have seen it already.
@andy.fingerhut thanks, that cleared it up. I thought that if it's lazy it's mentioned explicitly. So in absence of the mention lazy I always assumed the sequence is evaluated eagerly.
If a function returns a Clojure map, vector, or set, it is fully evaluated before returning -- there are no such lazy objects in Clojure. Sequences are not always returned as lazy, but usually are. Sometimes the docs say so explicitly, but apparently sometimes not.
If you look at the definition of subseq
e.g. by doing (source subseq)
in a REPL session, you will see that the final expressions returned are from (take-while ...)
, and take-while
is documented to return a lazy sequence
I wonder if you could use a spliterator on the underlying structure via interop
it's more of a Cursive question rather than a Clojure question. If I write tests about another clj file, do I need to reload the file in the REPL before running the test?
@dierre_spam In general, yes. Not sure whether Cursive has any automated reload-on-save logic. But it's a really good habit to get used to always evaluating each top-level form you change as you are editing. It's going to be just a hot key to press and after a while it will become second nature. Edit, eval, edit, eval. Then your REPL always reflects the state of your code.
And if you evaluate forms as you edit them, then you don't even need to save the file each time. I often edit, eval, edit, eval, switch to the test file, run tests (another hot key), switch back to source file, edit, eval, ... save.
You'll hear people talk about a REPL-Driven Development workflow but what they really mean is a workflow where your REPL is always up-to-date: you eval every single small change as you make it, so you can always test your code as you work. It's often helpful to have "Rich Comment Forms" in your source code that contain expressions that exercise your functions so you can easily eval those and check your results:
(comment
(my-func 123))
The ideal workflow is never typing directly into the REPL -- type into source/test files and evaluate code from there instead. And use (comment ,,,)
forms for "scratch"/test code so you don't accidentally leave code exploration at the top-level of a file.
Hey friends, I find that when I write code I end up with a big stack of declare statements at the tops of my files to avoid problems with forward declaration. Is this expected, or am I maybe organizing things oddly?
generally the declaration order required by clj means that clj files are organised from highest level at the bottom to lowest level at the top
I'm guessing you're striving to order it the opposite way (as recommended in clean code etc) and ending up with this
Ok. I guess I organize by subject, so I have a collection of functions for each subdomain in my project. I don't seem to be able to write those collections entirely independent from one another, a function that was written to interpret json data might also be useful for the game ai
I'll give that a try then, thanks the project is getting a lot bigger every week so it's starting to make more sense to split stuff up
and then within those, you have kind of smaller, building block funcs at the top? I guess if I start wanting to share those between files I can put them in their own namespace
many advantages to that. source control, extra names, when you use them from a different file you get to call them by something like subject/function-name etc. "Namespaces are one honking great idea—let's do more of those"
Hi all. Question: I am trying to map a 1d vector to a 2d vector so that [1 2 3 4] and [[5] [6] [7] [8]] produces [[1 5] [2 6] [3 7] [4 8]]. Any suggestions?
(cmd)(user=> (map cons [1 2 3 4] [[5] [6] [7] [8]])
((1 5) (2 6) (3 7) (4 8))
(ins)user=> (map (comp vec cons) [1 2 3 4] [[5] [6] [7] [8]]) ; in case vector is mandatory
([1 5] [2 6] [3 7] [4 8])
@noisesmith Perfect! Thank you, sir!
map takes any number of cols (they become the respective args on each call of the f passed), and cons adds a new element at the front of some other coll to make a list
Is there something similar to Javadoc for Clojure? Main benefit being your editor can give your internal references and info
@U050MP39D Thanks
If you want to produce good documentation for others who use your (open source) code, take a look at http://cljdoc.org which combines your docstrings with additional docs in the GitHub repo -- and also supports Markdown in docstrings and linking to other functions and docs etc.
Also for example cider can jump into definitions of symbols in docstrings at least if you wrap them in backticks
@andy.fingerhut thanks, I wasn't aware of the subseq
and that it runs in O(log n). So you are saying something like (first (rsubseq (sorted-map .....) <= x)))
in only O(log n) even if the subsequence returned by rsubseq
might be quite large?
I’m reading about transducers from this site: https://labs.uswitch.com/transducers-from-the-ground-up-the-practice/
And it seems to read like transducers are applied in the “wrong” direction when using comp
. I double checked in the repl, and their output seems correct. Why aren’t they applied right-to-left?
It’s a stack, kind of like middleware
each transducer takes another transducer as an arg, returning a new transducer
a different arity is used for the actual transform
the arities are documented here https://clojure.org/reference/transducers#_creating_transducers
(comp (map inc) (filter odd?))
filter odd is passed the context
that seems to inc first, then filter
which seems backwards to me
then map inc is passed the result of filter odd acting on the context
this returns a transducer which when used, maps then filters
see my doc link above
this part of the doc that goes step by step might be clearer https://clojure.org/reference/transducers#_defining_transformations_with_transducers
each one takes the previous transform as an arg, and decides whether to call it on the data it sees
> Composition of the transformer runs right-to-left but builds a transformation stack that runs left-to-right (filtering happens before mapping in this example).
the middleware pattern might be helpful to think of here if you are familiar
okay, that’s helping me
I’m not totally confident about how it fits in my brain, but I’m still reading some of these docs
at least it’s documented somewhere and I’m not totally crazy 😅
from the "creating transducers" section:
(fn [rf]
(fn ([] ...)
([result] ...)
([result input] ...)))
rf is the "continuation" or "target" - the transducing context, which wraps the previously composed transducers if anythe transducer should call rf on its input (unless you are filtering, then you skp calling rf for this data)
and of course you might also transform the input before calling rf on it
Okay, I think I got it now
Thanks for the help @noisesmith! 🙂
glad I could assist