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

Map a function on every two elements of a list

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

(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 和多个参数来处理分区集合的最后一个元素是单例(输入的长度为奇数)的情况。

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

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

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

一个 ,您可以稍微简化您的 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)))))))

这使用了几个常用技巧:

  • 如果累积列表的顺序错误,请使用向量 相反。
  • 如果可能,在条件中分解出共同元素。