如何在迭代中访问seq中的两个相邻元素

时间:2013-04-24 02:42:05

标签: clojure

我有一个seq (2 3 1 4)

我想迭代它,并且当下一个元素更小时,prev元素将替换elems另一个seq。 '( - 4 1)。

所以f('(2 3 1 4))=> (2( - 3 1)4)。我怎么写呢?

基本上 -

1)我想同时访问seq中的两个相邻元素。 2)编辑并返回该点的新seq。 3)继续处理新返回的seq。

通常实现上述3的机制是什么。 (map,reduce都让我一次只能访问一个elem。)

4 个答案:

答案 0 :(得分:5)

这实际上并不是成对消费的问题,因为您对序列进行分区的方式取决于您对当前对的处理方式。请注意,有时您会从序列(2 3)中使用一个项,但有时您会使用两个项(3 1)。

因此,您无法使用在clojure中创建滑动窗口的任何常用方法((partition 2 1 coll)(map fn coll (rest coll))等)。你需要使用明确递归的东西。

你的算法应该是这样的:

  1. 如果空seq发出nil
  2. 如果只有一个项目发出项目
  3. 如果有两个项目,请测试尺寸:
    1. 如果第一个>第二,用(- first second)替换两者;递减剩余物品
    2. 否则,先排出;使用第二件和剩余物品进行递归。
  4. 这是此算法的惰性实现。您也可以使用recur和累加器执行此操作 - 它不会是懒惰但它仍然有效。

    (defn combine-if-gt-next
      [[f s & r]]
      (cond
       (nil? f) nil
       (nil? s) (cons f nil)
       (> f s)  (cons (list '- f s) (lazy-seq (combine-if-gt-next r)))
       true (cons f (lazy-seq (combine-if-gt-next (cons s r))))))
    

    示例:

    (combine-if-gt-next '(2 3 1 4)) ; your example
    ;; (2 (- 3 1) 4)
    (combine-if-gt-next [])
    ;; nil
    (combine-if-gt-next [1 2 3])
    ;; (1 2 3)
    (combine-if-gt-next [2 3 4 1])
    ;; (2 3 (- 4 1))
    (combine-if-gt-next [2 3 4 1 4])
    ;; (2 3 (- 4 1) 4)
    (combine-if-gt-next [2 1 4 1 4 5 1])
    ;; ((- 2 1) (- 4 1) 4 (- 5 1))
    (combine-if-gt-next [5 4 3 2 1])
    ;; ((- 5 4) (- 3 2) 1)
    

答案 1 :(得分:1)

我不确定我是否完全明白了你的问题,但我会抓住它。

首先我们声明一个var来保存你的seq:

(def myseq '(2 3 1 4))

然后,我们可以将相同的序列压缩在一起,但是在不同的起点。 这样我们就可以轻松访问前一个元素。这将返回一系列seqs,这就是为什么我们使用mapcat来连接一个列表中的结果:

(mapcat
 (fn [prev curr]
   (if (< prev curr)
     [`(~'- ~curr ~prev)]
     [curr]))
 myseq (drop 1 myseq))

;; evaluates to((- 3 2) 1 (- 4 1))

或者如果你是在我的第二个提议输出之后:

(mapcat
 (fn [prev curr]
   (if (< prev curr)
     [`(~'- ~curr ~prev)]
     []))
 myseq (drop 1 myseq))

;; evaluates to ((- 3 2) (- 4 1))

希望这有帮助。

<强>更新

好的,这可以为您提供所需的输出:

(mapcat
 (fn [prev curr idx]
   (cond
    (< curr prev) [`(~'- ~prev ~curr)]
    (= (+ idx 2) (count myseq)) [curr]
    :else [prev]))
 myseq (drop 1 myseq) (range (count myseq)))

;; evaluates to (2 (- 3 1) 4)

希望这就是你所追求的目标。

答案 2 :(得分:1)

partition和partition-all允许您将序列转换为批量元素,使用(partition 2 1 data)可以迭代对的滑动窗口。

(def data '(2 3 1 4))

(loop [res [] pairs (partition-all 2 1 data)]
 (if-let [[l r] (first pairs)]
     (if (and r (< r l))
         (recur (conj res ['- l r]) (drop 2 pairs))
         (recur (conj res l) (rest pairs)))
     res))

;; returns [2 [- 3 1] 4]

答案 3 :(得分:1)

这是否解决了您的需求:

(defn my-fun [[f s & r]]
  (let [[flip next] (cond (and (nil? f) (nil? s)) ['() r]
                          (nil? f) [(list s) r]
                          (nil? s) [(list f) r]
                          (< s f) [(list (list (- f) s)) r]
                          :else [(list f) (cons s r)])]
    (if (nil? r)
      (concat flip next)
      (concat flip (my-fun next)))))


(my-fun '(2 3 1 4)) 
=> (2 (-3 1) 4)
(my-fun '(6 3 5 4))
=> ((-6 3) (-5 4))