首页 > 解决方案 > `for` 作为函数而不是宏

问题描述

我想要一个forall函数,它的行为类似于for但将序列列表作为输入。

(forall (fn [arr]
            (prn arr))
        (range 10)
        (range 10)
        (range 10))
=> 
; [0 0 0]
; [0 0 1]
; [0 0 2]
;  ....
; [9 9 9]

有什么好办法写这个吗?

标签: clojure

解决方案


我想你想映射输入序列的笛卡尔积,对吧?如果是这样,您可以使用例如clojure.math.combinatorics/cartesian-product来自clojure.math.combinatorics库的函数创建笛卡尔积的惰性序列,然后对其进行映射。

(defn forall [f & seqs]
  (map f (apply clojure.math.combinatorics/cartesian-product seqs)))

当有多个序列时,这个函数或多或少类似于宏。for这是调用它的示例,以及您将获得的结果:

(forall (fn [arr]
          [:element arr])
        (range 3)
        (range 3)
        (range 3))
;; => ([:element (0 0 0)] [:element (0 0 1)] [:element (0 0 2)] [:element (0 1 0)] [:element (0 1 1)] [:element (0 1 2)] [:element (0 2 0)] [:element (0 2 1)] [:element (0 2 2)] [:element (1 0 0)] [:element (1 0 1)] [:element (1 0 2)] [:element (1 1 0)] [:element (1 1 1)] [:element (1 1 2)] [:element (1 2 0)] [:element (1 2 1)] [:element (1 2 2)] [:element (2 0 0)] [:element (2 0 1)] [:element (2 0 2)] [:element (2 1 0)] [:element (2 1 1)] [:element (2 1 2)] [:element (2 2 0)] [:element (2 2 1)] [:element (2 2 2)])

请注意,在您的示例代码中,您调用的prn函数是有副作用的,而for-macro 会产生一个惰性序列。通常不鼓励将惰性与副作用结合起来,因为除非您知道惰性序列是如何工作的,否则副作用可能不会在您期望它发生时发生。

如果不想拉clojure.math.combinatorics入库,可以轻松实现自己的笛卡尔积函数,例如

(defn cart2 [a b]
  (for [x a
        y b]
    (conj x y)))

(defn my-cartesian-product [& arrs]
  (reduce cart2 [[]] arrs))

推荐阅读