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

How to rewrite Ruby's `Array(x)` in Clojure?

在Ruby中,有一个Kernel#Array方法,其作用如下:

1
2
3
4
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的对象)转换为数组。

我想在Clojure中有类似的东西:

1
2
3
4
(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

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

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

但我不喜欢它,因为:

  • seqable?函数在整体clojure-contrib爆炸期间蒸发。我不想在我的项目中仅包含一个功能就包含了庞大的,不再受支持的库。
  • 我感觉必须在clojure.core或clojure.contrib.whatever中都有一个内置函数。或者,还有更惯用的方法。
  • to-seq的确切输出类型并不重要。最主要的是,我想在列表推导中使用它的输出:

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

    因此,如果将其作为矢量-可以。如果它将是向量,Java数组,列表或nil(取决于输入),也可以。

    ==== UPD ====

    几乎是最终版本:

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

    最终版本:

    <罢工>
    (defn to-seq ..blabla..)

    我不需要序列,它看起来没有吸引力。


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

    话虽如此,我相信内核中实际上没有现成的函数来确定对象是否是seq的有效输入。相关的代码驻留在clojure.lang.RTseqseqFrom方法中,并且基本上依次尝试了几种可能性。我相信seqable?检查了相同的可能性-您可以执行相同的操作。现有的函数,例如coll?seq?sequential?都无法捕获至少某些情况(尤其是数组和字符串); coll?可能最接近,flatten尤其使用sequential?(因此,实际上限制其扁平化功能的能力)。

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

    1
    2
    3
    4
    5
    6
    7
    (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

    您也可以执行以下操作来加快速度(希望-我一定会进行基准测试):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    (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.seqRT.seqFrom使用的清单:

  • 如果输入已经是一个序列(对RT.seq来说是" clojure.lang.ASeq的实例",而不是ISeq),则返回原样;

  • 如果是延迟序列,则将其强制执行并返回结果;

  • 否则,如果它实现Seqable,则要求生成其内容的序列;

  • 如果恰好是nil(null),则返回nil

  • 如果它是Iterable,则返回迭代器seq;

  • 如果是数组,则返回数组seq;

  • 如果它是CharSequence,则返回字符串seq;否则,返回字符串seq。

  • 如果它是java.util.Map,则返回其条目集上的序列;

  • 否则将引发异常。


  • 1
    2
    3
    4
    (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?