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

时间:2016-03-19 18:39:24

标签: 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 (first k)) 
                       (first k)))))))

有没有更好的方法来实现这个目标?

4 个答案:

答案 0 :(得分:15)

(map #(% %2) 
     (cycle [f identity])
     coll)

答案 1 :(得分:1)

在使用循环和重复之前查看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,加上多个arities,以处理分区集合的最后一个元素是单例的情况(输入的长度是奇数)。

答案 2 :(得分:1)

另外,既然您想将函数应用于集合中的某些特定指示项(在这种情况下甚至是索引),您可以使用map-indexed,如下所示:

(defn flipflop [f coll]
  (map-indexed #(if (even? %1) (f %2) %2) coll))

答案 3 :(得分:1)

虽然amalloy's solution ,但您可以稍微简化loop - recur解决方案:

(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)))))))

这使用了几个常见的技巧:

  • 如果累积列表的顺序错误,请使用向量 代替。
  • 在可能的情况下,将条件中的常见元素分解出来。