1

在 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_aryor的对象#to_a)转换为数组。

我想在 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. 在整体 clojure-contrib爆炸期间,该seqable?功能消失了。我不想仅仅为了一个功能在我的项目中包含庞大的、不再受支持的库。
  2. 我觉得clojure.coreclojure.contrib.whatever中必须有一个内置函数。或者有更惯用的方法。

的确切输出类型to-seq并不重要。主要是我想在列表推导中使用它的输出:

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

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

==== 更新 ====

几乎是最终版本:

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

最终版本:

(defn to-seq ..blabla..)

我不需要排序,它看起来不吸引人。

4

2 回答 2

3

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

话虽如此,我相信核心中实际上没有现成的函数来确定对象是否是seq. 相关代码在clojure.lang.RT'sseqseqFrommethods中,基本依次尝试几种可能;我相信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以下是目前使用的清单RT.seqFrom

  1. 如果 input 已经是一个 seq (这RT.seq意味着“一个实例clojure.lang.ASeq”,不是ISeq),它被原封不动地返回;

  2. 如果是惰性seq,则强制执行并返回结果;

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

  4. 如果恰好是nil( null),nil则返回;

  5. 如果是Iterable,则返回迭代器 seq;

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

  7. 如果是 a CharSequence,则返回一个字符串 seq;

  8. 如果是 a java.util.Map,则返回其条目集的 seq ;

  9. 否则抛出异常。

于 2013-08-06T06:40:33.740 回答
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?

于 2013-08-06T06:00:11.923 回答