如何在Clojure中重写Ruby的`Array(x)`?

时间:2013-08-06 05:17:01

标签: clojure clojure-contrib

在Ruby中,有Kernel#Array方法,其行为如下:

Array([1, 2, 3]) #=> [1, 2, 3]
Array(1..5)      #=> [1, 2, 3, 4, 5]
Array(9000)      #=> [9000]
Array(nil)       #=> []

换句话说,它将nil转换为空数组,将非集合转换为单数组数组,以及将各种类似集合的对象(即响应#to_ary#to_a的对象)转换为数组。 / p>

我希望在Clojure中有类似的东西:

(to-seq [1 2 3])     ;=> (1 2 3)
(to-seq (range 1 5)) ;=> (1 2 3 4)
(to-seq 9000)        ;=> (9000)
(to-seq nil)         ;=> nil; () is ok too

这就是我到目前为止所得到的:

(defn to-seq [x] (if (seqable? x) (seq x) '(x)))

但我不喜欢它,因为:

  1. seqable?函数在整体clojure-contrib the explosion期间蒸发。我不想在我的项目中包含一个巨大的古代不再支持的库,仅用于一个功能。
  2. 我觉得必须有一个内置函数,可以在 clojure.core clojure.contrib.whatever 中。或者说有更多惯用的方法。
  3. to-seq的确切输出类型并不重要。主要的是,我想在列表推导中使用它的输出:

    (for [person (to-seq person-or-people)] ...)
    

    所以,如果将是一个矢量 - 它没关系。如果它将是一个向量,一个Java数组,一个列表或nil,具体取决于输入 - 这也很好。

    ==== UPD ====

    几乎最终版本:

    (defn to-seq [x] (if (or (nil? x) (coll? x)) x [x]))
    

    最终版本:

    <击>     (defn to-seq ..blabla..)

    我不需要-seq,它看起来并不吸引人。

2 个答案:

答案 0 :(得分:3)

如果您只需要迭代输入并且它实际上是某种容器,您通常不需要执行任何特殊转换。我想到的两个特殊情况由enumeration-seqiterator-seq处理。

话虽如此,我相信在核心中确实没有现成的函数来确定对象是否是seq的有效输入。相关代码位于clojure.lang.RT的{​​{1}}和seq方法中,基本上依次尝试了几种可能性;我相信seqFrom检查了相同的可能性 - 你也可以这样做。像seqable?coll?seq?这样的现有函数都无法捕获至少一些情况(特别是数组和字符串); sequential?可能最接近,coll?明显被sequential?使用(因此它实际上限制事物的能力有限)。

当然总是有可能尝试应用flatten并捕获异常(不是我推荐的):

seq

你也可以做这样的事情来加快速度(希望 - 我肯定是基准):

(defn seq-at-all-costs [x]
  (try
    (seq x)
    (catch IllegalArgumentException e
      (list x))))   ; NB. '(x) wouldn't work, since it would
                    ; cause a literal symbol x to be returned,
                    ; as noted in my comment on the question
但是,我不得不说,我几乎没有 - 也许实际上从未 - 感到需要(defprotocol IKnowHowToSeqYou (seq-knowledgeably [this])) (defn seq! [x] (try (seq-knowledgeably x) (catch IllegalArgumentException e (try (seq x) (extend-type (class x) IKnowHowToSeqYou (seq-knowledgeably [this] (seq this))) (catch IllegalArgumentException e (extend-type (class x) IKnowHowToSeqYou (seq-knowledgeably [this] (list this))))) (seq-knowledgeably x)))) 。原因可能是我完全不知道在任何给定点上我将要处理的对象类型是相对不寻常的,在极少数情况下我倾向于通过案例/使用协议/使用多方法等。


以下是截至目前seqable?RT.seq使用的核对清单:

  1. 如果输入已经是seq(RT.seqFrom表示“RT.seq”的实例,而不是clojure.lang.ASeq),则返回不变;

    < / LI>
  2. 如果它是一个懒惰的seq,它会被强制并返回结果;

  3. 否则,如果它实现ISeq,则要求它生成seq的内容;

  4. 如果恰好是Seqablenil),则会返回null;

  5. 如果它是nil,则返回迭代器序列;

  6. 如果是数组,则返回数组seq;

  7. 如果是Iterable,则返回字符串seq;

  8. 如果是CharSequence,则返回其条目集的seq;

  9. 否则会抛出异常。

答案 1 :(得分:2)

(map coll? [() [] #{} {} 1 "sandwich" :a nil])) 
;=> (true true true true false false false false)

(defn to-seq [x] (if (coll? x) (seq x) (list x)))

coll?有用吗?

另请参阅:seq?sequential?