【问题标题】:How to rewrite Ruby's `Array(x)` in Clojure?如何在 Clojure 中重写 Ruby 的 `Array(x)`?
【发布时间】:2013-08-06 23:48:59
【问题描述】:

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

我想在 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.coreclojure.contrib.whatever 中一定有一个内置函数。或者还有更惯用的方法。

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..)

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

【问题讨论】:

  • 附带说明,'(x) 计算为包含符号 x 的单例列表;你会想用(list x)[x] 代替。

标签: clojure clojure-contrib


【解决方案1】:

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

话虽如此,我相信核心中实际上没有现成的函数来确定对象是否是seq 的有效输入。相关代码驻留在clojure.lang.RTseqseqFrom方法中,基本上依次尝试几种可能;我相信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.seqRT.seqFrom 目前使用的清单:

  1. 如果输入已经是一个序列(RT.seq 的意思是“clojure.lang.ASeq 的一个实例”,而不是ISeq),则原样返回;

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

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

  4. 如果恰好是nilnull),则返回nil

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

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

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

  8. 如果是java.util.Map,则返回其入口集的seq;

  9. 否则抛出异常。

【讨论】:

  • 不过,我不得不说,我几乎从来没有——也许实际上从来没有——觉得需要seqable?是的,需要seqable?对我也不是很好。我将尝试重构代码,因此没有必要。同时,我将使用coll?,因为它符合我目前的需求。
【解决方案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?

【讨论】:

  • 是的,coll? 是合适的。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-03
  • 2012-09-22
  • 2010-09-26
  • 2010-11-14
  • 2013-03-09
  • 2014-01-21
  • 2011-07-11
相关资源
最近更新 更多