Idiomatic Clojure Python crop imitation

I repeat the list, creating a state when I go, and sometimes when I meet a certain sentinel, I return the result. If I did this in Python, I would be lazy at the yieldresults, the tracking state in the local area of ​​the function when I go:

# this is simplified for illustration
def yielder(input_list):
    state = 0
    for item in input_list:
        if item = 'SENTINEL':
            yield state * 2
            state = 0
        else:
            state += item

yielder([1, 5, 2, 5, 'SENTINEL', 4, 6, 7]) # [26, 34]

It’s used in my first implementation reduce, but it’s not as good as yieldbecause:

  • The value of I passing between iterations has both the state of the loop and the elements I want to get, which seems awkward
  • It's not lazy

iterate can be used to mitigate the latter, but actually I don’t want to return something for each input element, so this will require more munging.

What is the idiomatic way to do this in Clojure?

+4
4

, lazy-seq, , partition reduce, , . , :

user> (->> [1, 5, 2, 5, :SENTINEL, 4, 6, 7] ;; start with data
           (partition-by #(= :SENTINEL %))  ;; ((1 5 2 5) (:SENTINEL) (4 6 7))
           (take-nth 2)                     ;; ((1 5 2 5) (4 6 7))
           (map #(* 2 (reduce + %))))       ;; the map here keeps it lazy
(26 34)

lazy-seq:

user>  (defn x [items]
         (when (seq items)
           (lazy-seq (cons (* 2 (reduce + (take-while #(not= :SENTINEL %) items)))
                           (x (rest (drop-while #(not= :SENTINEL %) items)))))))
#'user/x
user> (x [1, 5, 2, 5, :SENTINEL, 4, 6, 7])
(26 34)
+3

Tupelo , lazy-gen/yield, Python:

(ns xyz
  (:require [tupelo.core :as t] ))

(def data-1 [1 5 2 5 :SENTINEL 4 6 7] )
(def data-2 [1 5 2 5 :SENTINEL 4 6 7 :SENTINEL] )

(defn yielder [vals]
  (t/lazy-gen
    (let [state (atom 0)]
      (doseq [item vals]
        (if (= :SENTINEL item)
          (do
            (t/yield (* 2 @state))
            (reset! state 0))
          (swap! state + item))))))

(yielder data-1) => (26)
(yielder data-2) => (26 34)

, , :SENTENEL. data-1 data-2 .

+1

Although I prefer the first Arthur solution, it can also be written in a lower-level style without using lazy-seq:

(defn f [xs]
  (loop [[x & xs :as xxs] xs, n 0, ret []]
    (cond (empty? xxs) (conj ret (* n 2))
          (= x :sentinel) (recur xs 0 (conj ret (* n 2)))
          :else (recur xs (+ n x) ret))))
0
source

Here is my version using reduce:

(def v [1 5 2 5 "SENTINEL" 4 6 7])

(defn r []
  (let [{:keys [result current]}
        (reduce (fn [acc x]
                  (case x
                    "SENTINEL" (-> acc
                                   (update-in [:result] conj (* 2 (:current acc)))
                                   (update-in [:current] (constantly 0)))
                    (update-in acc [:current] #(+ x %))))
                {:result [] :current 0} v)]
    (conj result (* 2 current))))

user> (r)
[26 34]
-1
source

Source: https://habr.com/ru/post/1524315/


All Articles