beginners 2025-09-30

In a pretty large data structure, I'd need to update a bunch of values. So given a set of keys #{:foo :bar :baz} that can be in unpredictable places in a data structure (but always keys in a map) I'd need to apply some function f to "update" the corresponding values. Is there some clojure.walk or such function that could help me do this?

I've not used it personally, but this sounds like the kind of problem https://github.com/redplanetlabs/specter might be useful for.

βž• 2

using clojure's walk fn you can check if a value is a MapEntry with the given key and update its value

πŸ‘ 1

walk + "a pretty large data structure" is not always the best combination if you care about performance

I thought pre/post/walk will feed my function the key and the value separately. Do those also pass in MapEntrys?

I'm not saying there's some single obvious alternative, it depends on the situation if you can avoid walking. For example, in one situation I had code walking over several large but structurally static(!) datastructures. I split the walking into separate "find out what paths to update" and "actually update the value behind each path" steps and got like 50-200x speed up because the paths were static and I calculated them just once.

I usually try to think "why don't I know the exact paths to the values I need to alter". Could the producer of the data provide some extra information which I could use later to construct the paths, so that I don't have to resort to walking? But of course, this isn't always possible.

βž• 2

I see your point. update-in just gets so clumsy when there are vectors involved somewhere in the structure

ChillPillzKillzBillz 2025-09-30T14:41:36.846269Z

Hello I am using tap> for printing strings from different core.async threads. This works quite well. That is until I put a for loop in a thread. The tap> inside a for loop doesn't seem to work. Is this standard behaviour? Or more likely I am being an idiot? The code I am trying out is as follows:

;; tap has been registered
(add-tap (bound-fn* clojure.pprint/pprint))

(go-loop [numbers (range 10 0 -1)]
  (for [n numbers]
    (do
      ( n))))
#object[clojure.core.async.impl.channels.ManyToManyChannel 0x7851a03f "clojure.core.async.impl.channels.ManyToManyChannel@7851a03f"] Whereas the following seems to work
(go-loop [numbers (range 10 0 -1)]
  (loop [n numbers]
    (Thread/sleep 10)
    (tap> (first n))
    (when (pos? (count n))
      (recur (rest n)))))
; 10
; 9
; 8
; 7
; 6
; 5
; 4
; 3
; 2
; 1
; nil
The reason I need to use a for loop inside a go-loop block is because otherwise I'll have to nest 2 loops and that is not allowed. Or is it?

Also note there's nothing that prevents nesting for or doseq expressions; for simple cases you can even use the built-in iteration over multiple variables:

(for [a [1 2 3]
      b [4 5 6]]
  [a b])
;; => ([1 4] [1 5] [1 6] [2 4] [2 5] [2 6] [3 4] [3 5] [3 6])
(doseq [x [1 2 3]]
  (doseq [y [4 5 6]]
    (println [x y])))
;; => nil
;; but prints:
[1 4]
[1 5]
[1 6]
[2 4]
[2 5]
[2 6]
[3 4]
[3 5]
[3 6]
The default looping construct in Clojure, loop, also nests, though it's a bit peculiar due to how recur works:
(loop [outer 3]
  (loop [inner outer]
    (when (pos? inner)
      (println inner)
      (recur (dec inner))))
  (when (pos? outer)
    (recur (dec outer))))
;; => nil
;; but prints:
3
2
1
2
1
1

Just noting that doseq supports the same syntax as for, so no need for nested doseq here - it can be a single doseq.

Yes; sorry if it wasn't clear, I just meant to show that there's nothing that prevents nesting.

πŸ‘ 1

there is no such thing as a for loop

ChillPillzKillzBillz 2025-09-30T14:46:52.210639Z

I saw the documentation of clojure.core/for and that is what I am using...

ChillPillzKillzBillz 2025-09-30T14:46:56.875179Z

is this wrong?

there is a for list comprehension that lazily builds a list. Which requires a consumer of that list

there is no such thing as a for loop in clojure core though

clojure.core/for
([seq-exprs body-expr])
Macro
  List comprehension. Takes a vector of one or more
   binding-form/collection-expr pairs, each followed by zero or more
   modifiers, and yields a lazy sequence of evaluations of expr.

ChillPillzKillzBillz 2025-09-30T14:47:56.306659Z

This is from the example blurb

ClojureDocs Examples
 via cider-nrepl

Example 1
To Rich Comment | To Output

;; prepare a seq of the even values 
;; from the first six multiples of three
(for [x [0 1 2 3 4 5]
      :let [y (* x 3)]
      :when (even? y)]
  y)
;;=> (0 6 12)

yes. there the repl’s printer is forcing the realization of the list. What is forcing the realization in your example?

ChillPillzKillzBillz 2025-09-30T14:48:34.574439Z

ok. Then do you have a way of nesting loops one inside the other?

functions usually

ChillPillzKillzBillz 2025-09-30T14:49:01.317549Z

ok

ChillPillzKillzBillz 2025-09-30T14:49:05.912069Z

I'll give it a try

Your example will work if you replace for with doseq. And then you also won't need that do.

πŸ’― 1
ChillPillzKillzBillz 2025-09-30T15:55:02.548919Z

@p-himik it did indeed

ChillPillzKillzBillz 2025-09-30T15:55:04.837639Z

thanks!