一個簡單問題的Clojure實現

這幾天試着用 PigPen 寫一些腳原本處理一些數據。過程當中遇到一個問題,感受永別的語言寫起來必定很簡單,可是因爲用 Clojure 寫代碼的經驗少,一時不知道怎麼用 Clojure 的方法來實現。git

爲了方便講清楚問題,先對問題進行簡化:github

有一個數組 [1 2 3 7 8 9 13 13 14],我想計算相鄰兩個元素的差,若是大於3,就把以前的元素拿出來做爲一個分組,最後返回一個包含全部分組的數組。如剛纔那個數組返回的結果但願是 [[1 2 3] [7 8 9] [13 13 14]]。數組

若是用 Lua,實現很簡單:數據結構

function my_partition(arr)
	local grps, cur_grp, last_idx = {}, {}, 1
	for idx,item in ipairs(arr) do
		if item - arr[last_idx] > 3 then
			table.insert(grps, cur_grp)
			cur_grp = {}
			table.insert(cur_grp, item)
		else
			table.insert(cur_grp, item)
		end
		last_idx = idx
	end
	table.insert(grps, cur_grp)
	return grps
end

可是若是用 Clojure 來實現,感受不太適合定義像 grpscur_grplast_idx這樣的狀態變量,由於 Clojure 中不少數據結構都是 immutable 的,是儘可能避免狀態變量的。並且剛開始寫 Clojure 就帶着壞習慣,寫慣了以後就很難改過來了。全部寧肯剛開始慢一點,也儘可能避免壞習慣。因此就向 cn-clojure郵件組 發了一封郵件問這個問題。app

<!--more-->google

最早回覆的是 Dennis,給出了兩個方案:atom

(let [prev (atom 0)]
    (partition-by #(let [v @prev]
                      (reset! prev %)
                      (> (-  % v) 3))
        [1 2 3 7 8 9 13 13 14]))

這個方案中仍是用到了一個狀態變量 prev,還有另外一個方案:lua

(defn my-partition
  ([coll]
     (my-partition [] coll))
  ([xs coll]
  (lazy-seq
   (when-let [s (seq coll)]
     (let [fst (first s)
           sec (second s)]
       (if (or (nil? sec) (> (- sec fst) 3))
         (cons (conj xs fst) (my-partition [] (next s)))
         (my-partition (conj xs fst) (next s))))))))

這是個 lazy-seq 的方法,利用相似尾遞歸的方法返回 lazy 的分組,這種方法和 clojur.core 裏的 partition-by 方法的實現相似,也是我看了 partition-by 的實現後想到的一種方法,可是本身沒寫下來。.net

還有其餘一些同窗的答案:code

(reduce (fn [result i]
          (let [prev (-> result last last)]
            (if (and prev (<= (- i prev) 3))
              (conj (pop result) (conj (peek result) i))
              (conj result [i]))))
        []
        [1 2 3 7 8 9 13 13 14])

還有:

(let [x [1 8 3 6 10 14 11 15]]
  (->> (partition 2 1 x)
       (map #(- (second %) (first %)))
       (map (partial < 3))
       (interleave x )
       (apply vector)
       (#(conj % (last x)))
       (partition-by #(or (number? %) (false? %)))
       (map #(filter number? %))
       (filter seq)))

還有:

(defn f [coll]
  (->> (conj coll (last coll))
       (partition 2 1)
       (keep-indexed #(if (> (- (second %2) (first %2)) 3) %1))
       (cons -1)
       (partition 2 1)
       (map #(- (second %) (first %)))
       (reduce #(concat (butlast %1) (split-at %2 (last %1))) [coll])))

每一個看完後都感受受益不淺。

相關文章
相關標籤/搜索