beginners

valerauko 2025-09-30T13:44:06.077119Z

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?

gaverhae 2025-10-01T09:10:48.799829Z

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

βž• 2
Roman Liutikov 2025-09-30T13:45:45.586219Z

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

πŸ‘ 1
lassemaatta 2025-09-30T13:49:26.825629Z

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

valerauko 2025-09-30T13:50:11.068719Z

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

Roman Liutikov 2025-09-30T13:57:57.579709Z

@vale yep

1
valerauko 2025-09-30T14:15:06.485999Z

@lasse.olavi.maatta what else could I do?

lassemaatta 2025-09-30T14:25:18.033009Z

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.

lassemaatta 2025-09-30T14:29:27.917839Z

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
valerauko 2025-09-30T16:34:33.235079Z

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?

gaverhae 2025-10-01T09:21:50.425349Z

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

p-himik 2025-10-01T09:24:27.563919Z

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

gaverhae 2025-10-01T09:44:00.865779Z

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

πŸ‘ 1
dpsutton 2025-09-30T14:45:45.402219Z

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?

dpsutton 2025-09-30T14:47:11.351179Z

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

dpsutton 2025-09-30T14:47:20.627119Z

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

dpsutton 2025-09-30T14:47:51.622849Z

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)

dpsutton 2025-09-30T14:48:19.949449Z

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?

dpsutton 2025-09-30T14:48:47.735499Z

functions usually

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

ok

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

I'll give it a try

p-himik 2025-09-30T14:53:45.661169Z

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!