1

我有一个 seq (2 3 1 4)

我想遍历它,并且无论下一个元素小于哪个元素,那么 prev 元素都会替换两个 elems 另一个 seq。'(- 4 1)。

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

基本上 -

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

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

4

4 回答 4

5

这实际上不是成对消耗的问题,因为您如何划分序列取决于您对当前对执行的操作。请注意,有时您会从序列中消费一项2 3( ),但有时您会消费两项( 3 1)。

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

你的算法应该是这样的:

  1. 如果空 seq 发出 nil
  2. 如果只有一个项目发出项目
  3. 如果两个项目,测试大小:
    1. 如果 first > second,将两者都替换为(- first second); 用剩余项目递归
    2. 否则,先发射;递归第二个和剩余的项目。

这是该算法的惰性实现。你也可以用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)
于 2013-04-24T06:42:00.317 回答
1

我不确定我是否完全理解您的问题,但我会尝试一下。

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

(def myseq '(2 3 1 4))

然后,我们可以将相同的序列压缩在一起,但起点不同。这样我们就可以轻松访问前一个元素。这将返回一个 seq 的 seq,这就是我们用来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)

希望这就是你所追求的。

于 2013-04-24T03:10:13.357 回答
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]
于 2013-04-24T05:01:03.377 回答
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))
于 2013-04-24T05:20:03.377 回答