在Clojure中表达Python的功能/惯用方法

时间:2016-05-26 23:18:58

标签: python clojure transformation

我的数据转换让我有点陷入困境。我无法在Clojure甚至我的Python中表达自己,我能说得很流利,但仍感觉非常粗暴。

我需要一个像:

这样的数据结构
[1, 2, [3, 4], 5, 6]

生成这样的结构:

[[1, 2, 3, 5, 6]
 [1, 2, 4, 5, 6]]

其中每个子集合创建一个新集合,其中包含迄今为止累积的项目。我只期望一个级别的嵌套。

我的python尝试看起来像这样:

def foo(path, acc_list=[[]]):
    for val in path:
        if not isinstance(val, list):
            for acc in acc_list:
                acc.append(val)
        else:
            for subval in val:
                new = acc_list[0][:]
                new.append(subval)
                acc_list.append(new)
            del acc_list[0]
    return acc_list

foo([1, 2, [3, 4], 5, 6])
# => [[1, 2, 3, 5, 6], [1, 2, 4, 5, 6]]

我想知道Clojure解决方案将会是什么以及(更重要的是)导致该解决方案的想法。

更新

  • 例如,它们可能是关键字或字符串,也不一定是有序的,但显然必须保留订单。

  • 通过嵌套,我的意思是它不像[1 [2 [3 [4 5] 6] 7] 8],但更像是[1 [2 3] 4 [5] 6 [7 8 9]] - 浅。

2 个答案:

答案 0 :(得分:2)

许多Clojure核心库函数的一个重要特性是能够处理惰性(可能是无限的)序列;因此,我认为一个好的(惯用的)Clojure解决方案能够正确扩展包含无限延迟序列的子序列的输入。例如:

[:a :b (range) :c]

应该扩展到

((:a :b 0 :c) (:a :b 1 :c) (:a :b 2 :c) (:a :b 3 :c) ...)

如果顶级序列也可能是无限的并且懒惰地处理,那将是很好的,但是,我不认为这个问题是可能的。 (但如果其他人能想出一种实际处理方法,我会感到非常惊讶!)

这是我的解决方案:

(defn expand-subseqs [[x & xs]]
  (when-not (nil? x)
    (let [heads (if (sequential? x) x [x])
          tails (expand-subseqs xs)]
      (if-not tails (map vector heads)
        (for [z tails, y heads] (cons y z))))))

这里的直觉是你先递归处理输入序列的尾部,然后将当前头部的每个可能值添加到每个可能的尾部。

一些示例输出:

user=> (expand-subseqs [1, 2, [3, 4], 5, 6])
((1 2 3 5 6) (1 2 4 5 6))
user=> (take 5 (expand-subseqs [:a :b (range) :c [true false]]))
((:a :b 0 :c true) (:a :b 1 :c true) (:a :b 2 :c true) (:a :b 3 :c true) (:a :b 4 :c true))

这个解决方案的一个很好的好处是,通过使用cons,我们实际上重用了表示每个结果的尾部序列的对象,而不是为每个排列重复整个序列。例如,在上面的最后一个示例中,五个输出中每个输出中的(:c true)尾部序列实际上是同一个对象。

答案 1 :(得分:1)

我确信还有其他方法可以做到这一点,但这是一种方式。

user.core=> (def x [1, 2, [3, 4], 5, 6])
#'user.core/x
user.core=> (defn create-vecs [x]
         =>   (let [sub (first (filter vector? x))
         =>         all (remove vector? x)]
         =>   (vec (map #(vec (sort (conj all %))) sub))))
#'user.core/create-vecs
user.core=> (create-vecs x)
[[1 2 3 5 6] [1 2 4 5 6]]

基本上,你抓取向量元素和向量的其余部分减去向量元素,然后映射它们以创建带有conj的两个新向量。您需要额外的vec,因为filterremove会返回列表,而不是向量。