1

In Java I can do this

list.stream().peek(System.out::println).filter(i -> i >= 0).findFirst();

This will find the first positive number in a list, printing all numbers since the begining until the first positive number.

I need something similar in Clojure, but I can't find a equivalent for peek. peek does something different in Clojure. I need to create a side effect without consuming the sequence, intermediary. Just as elements pass through, they should be passed to a function. I could implement it myself, but I don't want to reinvent something existing. Seems like basic functionality, I was unable to find the right function to call).

Ionut Bilica
  • 424
  • 1
  • 5
  • 11
  • 2
    you could make up some function for effect only, like this: `(defn effect [f] (fn [x] (f x) x))`. then use it this way: `(first (drop-while (comp neg? (effect println)) (range -10 10)))`. But there is a pitfall here: in certain cases clojure optimizes the consumption of sequences, realizing them by chunks. So `(first (filter (comp pos? (effect println)) (range -10 10)))` would give you more output then you expect. As long as you use take-while / drop-while it is ok, but filter / map and others would require some de-chunking code to ensure no unexpected side effect would happen. – leetwinski Oct 05 '20 at 14:16

2 Answers2

1

Just use map in combination with doto and transducers to have it do a side effect and otherwise be a passthrough:

(into []
 (comp
  (map #(doto % println))
  (filter pos?)
  (take 1))
 [-1 -2 -3 4 5])

Or for what you said in your comment you can use do for example:

(->>
 (repeatedly #(rand-nth [1 nil nil]))
 (into []
  (comp
   (map #(do (when (nil? %) (Thread/sleep 1000)) %))
   (remove nil?)
   (take 1)))
 first)

I'm using transducers, because lazy sequences are not guaranteed to be one at a time, due to chunking, and special care is needed if you want to avoid that, so it's just much better and easier in a case where you want to mix in side-effects inside a data transformation pipeline to use transducers instead.

Didier A.
  • 4,609
  • 2
  • 43
  • 45
0

If you study the Clojure CheatSheet, you can find the function split-with which does pretty much what you want:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test))

(dotest
  (let [[dropped kept] (split-with #(neg? %) (range -10 10))]
    (is= dropped [-10 -9 -8 -7 -6 -5 -4 -3 -2 -1])
    (is= kept [0 1 2 3 4 5 6 7 8 9])
    (is= 0 (first kept))))

If you need something in the future and can't find a built-in function that works, you can always write simple loops (or "recursion") using loop/recur.

Please also see this list of documentation.

Alan Thompson
  • 29,276
  • 6
  • 41
  • 48
  • It was just an example, what I really need to know is if there's something close to Java Stream peek in Clojure. I can implement this myself, I can copy the implementation of map, call f but don't use the result, let the original element pass. It feels like basic functionality: to be able to peek inside a chain of transducers. My real usage is more exotic, I want to wait one second if the current element is nil at an intermediary step in the transducers chain. Something like this: (->> (repeatedly step) *** (remove nil?) first) where *** would be something like (javapeek sleepOneSecIfNil). – Ionut Bilica Oct 06 '20 at 07:22