Clojure 在列表的每两个元素上映射一个函数

Clojure 在列表的每两个元素上映射一个函数,clojure,map-function,Clojure,Map Function,我需要一个只在其他元素上映射函数的函数,例如 (f inc '(1 2 3 4)) => '(2 2 4 4) 我想到了: (defn flipflop [f l] (loop [k l, b true, r '()] (if (empty? k) (reverse r) (recur (rest k) (not b) (conj r (if b

我需要一个只在其他元素上映射函数的函数,例如

(f inc '(1 2 3 4)) 
=> '(2 2 4 4)
我想到了:

(defn flipflop [f l]
  (loop [k l, b true, r '()]
    (if (empty? k)
      (reverse r)
      (recur (rest k) 
             (not b) 
             (conj r (if b 
                       (f (first k)) 
                       (first k)))))))

有没有更好的方法来实现这一点?

在使用循环和重现之前,最好先看看Clojure的高级函数

user=> (defn flipflop
         [f coll]
         (mapcat #(apply (fn ([a b] [(f a) b])
                             ([a] [(f a)]))
                         %)
                 (partition-all 2 coll)))
#'user/flipflop

user=> (flipflop inc [1 2 3 4])
(2 2 4 4)

user=> (flipflop inc [1 2 3 4 5])
(2 2 4 4 6)

user=> (take 11 (flipflop inc (range))) ; demonstrating laziness
(1 1 3 3 5 5 7 7 9 9 11)
这个触发器不需要反转输出,它是懒惰的,我发现它更容易阅读

该函数使用
partition all
将列表拆分为两个项目对,并使用mapcat将调用中的一系列两元素序列连接回单个序列


该函数使用apply加上多个算术运算,以处理分区集合的最后一个元素为单例(输入长度为奇数)的情况。

此外,由于要将该函数应用于集合中的某些特定标记项(在这种情况下为偶数索引),因此可以使用
映射索引,像这样:

(defn flipflop [f coll]
  (map-indexed #(if (even? %1) (f %2) %2) coll))
鉴于此,您可以稍微简化
循环
-
重复
解决方案:

(defn flipflop [f l]
  (loop [k l, b true, r []]
    (if (empty? k)
      r
      (recur (rest k) 
             (not b) 
             (conj r ((if b f identity) (first k)))))))
这使用了两个常见技巧:

  • 如果累积列表的顺序错误,请使用向量 相反
  • 在可能的情况下,将公共元素分解为条件

比我的选择(为了比较和启发起见,我留下了这个选择)要好得多,确实非常简洁:),但你的选择也很有趣。
(defn flipflop [f l]
  (loop [k l, b true, r []]
    (if (empty? k)
      r
      (recur (rest k) 
             (not b) 
             (conj r ((if b f identity) (first k)))))))