簡體   English   中英

Clojure:具有可更新狀態的地圖功能

[英]Clojure: map function with updatable state

在對每個序列元素的功能應用之間實現映射功能以及可更新狀態的最佳方法是什么? 為了說明這個問題,讓我們假設我們有以下問題:

我有一個數字向量。 我想要一個新的序列,其中每個元素乘以2,然后在序列中增加10的數量,直到並包括當前元素。 例如來自:

[20 30 40 10 20 10 30]

我想生成:

[40 60 80 21 41 22 62]

在不增加10的情況下,可以使用高級抽象來制定解決方案:

(map #(* 2 %) [20 30 40 10 20 10 30])

計數更新迫使我“轉為基本”,而我想到的解決方案是:

(defn my-update-state [x state]
  (if (= x 10) (+ state 1) state)
  )

(defn my-combine-with-state [x state]
  (+ x state))

(defn map-and-update-state [vec fun state update-state combine-with-state]
  (when-not (empty? vec)
    (let [[f & other] vec
          g (fun f)
          new-state (update-state f state)]
      (cons (combine-with-state g new-state) (map-and-update-state other fun new-state update-state combine-with-state))
      )))


(map-and-update-state [20 30 40 50 10 20 10 30 ] #(* 2 %) 0 my-update-state my-combine-with-state )

我的問題是:解決問題的適當/規范方法,還是我忽略了一些重要的概念/功能。

PS:

  1. 最初的問題是遍歷AST(抽象語法樹)並生成新的AST,同時更新符號表,因此在提出上述問題的解決方案時,請記住這一點。

  2. 我不必擔心會炸毀堆棧,因此在這里我不必擔心用loop + recur進行替換。

  3. 使用全局Vars或Refs而不是將state作為參數傳遞是絕對否定的嗎?

您可以使用reduce來累加到目前為止看到的10的數量和結果的當前向量對。

(defn map-update [v]
  (letfn [(update [[ntens v] i]
             (let [ntens (+ ntens (if (= 10 i) 1 0))]
               [ntens (conj v (+ ntens (* 2 i)))]))]
    (second (reduce update [0 []] v))))

要計算10的數量,您可以做

(defn count-10[col]
  (reductions + (map #(if (= % 10) 1 0) col)))

例:

user=> (count-10 [1 2 10 20 30 10 1])
(0 0 1 1 1 2 2)

然后是一張最終結果的簡單地圖

(map + col  col (count-10 col)))

約簡和約簡是遍歷保持狀態的序列的好方法。 如果您覺得代碼不清楚,則可以始終將遞歸與循環/遞歸或惰性序列一起使用

(defn twice-plus-ntens
  ([coll] (twice-plus-ntens coll 0))
  ([coll ntens]
    (lazy-seq
      (when-let [s (seq coll)]
        (let [item (first s)
              new-ntens (if (= 10 item) (inc ntens) ntens)]
          (cons (+ (* 2 item) new-ntens) 
                (twice-plus-ntens (rest s) new-ntens)))))))

看看地圖源代碼,在您的repl上對此進行評估

(source map)

我跳過了分塊優化和多集合支持。

您可以通過這種方式使它成為高階函數

(defn map-update
  ([mf uf coll] (map-update mf uf (uf) coll))
  ([mf uf val coll]
    (lazy-seq
      (when-let [s (seq coll)]
        (let [item (first s)
              new-status (uf item val)]
          (cons (mf item new-status) 
                (map-update mf uf new-status (rest s))))))))

(defn update-f
  ([] 0)
  ([item status]
    (if (= item 10) (inc status) status)))

(defn map-f [item status]
  (+ (* 2 item) status))

(map-update map-f update-f in)

最合適的方法是將函數與狀態一起使用

(into
  []
  (map
    (let [mem (atom 0)]
      (fn [val]
        (when (== val 10) (swap! mem inc))
        (+ @mem (* val 2)))))
  [20 30 40 10 20 10 30])

也看到

memoize

標准功能

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM