【问题标题】:Clojure loop collectionClojure 循环集合
【发布时间】:2017-12-14 10:58:53
【问题描述】:

我想知道这是否是循环遍历集合的正确方法:

(def citrus-list ["lemon" "orange" "grapefruit"])

(defn display-citrus [citruses]
  (loop [[citrus & citruses] citruses]
    (println citrus)
    (if citrus (recur citruses))
    ))

(display-citrus citrus-list)

我有三个问题:

  1. 最终打印显示为 nil,是否可以或如何避免?
  2. 我了解 & 在此示例中的作用,但在其他情况下我看不到,也许您可​​以提供一些示例
  3. 还有其他例子可以得到相同的结果吗?

谢谢, R.

【问题讨论】:

  • 这取决于你想对集合中的每个项目做什么。如果您只想像在这里一样打印,那么doseq 是一个不错的选择。但是mapreduceloop/recuriterate 都可以遍历集合中的所有项目。

标签: clojure


【解决方案1】:

首先你的实现是错误的。如果您的列表包含nil,它将失败:

user> (display-citrus [nil "asd" "fgh"])
;;=> nil
nil

如果列表为空,则打印不需要的 nil:

user> (display-citrus [])
;;=> nil
nil

你可以这样修复它:

(defn display-citrus [citruses]
  (when (seq citruses)
    (loop [[citrus & citruses] citruses]
      (println citrus)
      (if (seq citruses) (recur citruses)))))

1) 完全没问题:对于非空集合,函数内部的最后一次调用是println,它返回nil,对于空集合,您不调用任何内容,这意味着将返回nil ( clojure 函数总是返回一个值)。为避免在您的情况下出现 nil ,您应该显式返回一些值(例如这样):

(defn display-citrus [citruses]
  (when (seq citruses)
    (loop [[citrus & citruses] citruses]
      (println citrus)
      (if (seq citruses) (recur citruses))))
  citruses)

user> (display-citrus citrus-list)
;;=> lemon
;;=> orange
;;=> grapefruit
["lemon" "orange" "grapefruit"]

2) 一些关于destructuring 的文章应该对你有所帮助

3) 是的,有一些方法可以做到这一点。最简单的是:

(run! println citrus-list)

【讨论】:

  • 显然。我只是想让它尽可能接近操作的变体,这样他就可以很容易地看到差异
【解决方案2】:

回答您的最后一个问题,您应该避免在 Clojure 中使用 loop。这种形式更适合真正知道自己在做什么的有经验的用户。在您的情况下,您可以使用doseq 等更易于使用的表单。例如:

(doseq [item collection]
  (println item))

您也可以使用map,但请记住,它有时会返回一个新列表(如果您的情况是nils),这有时是不可取的。比如说,您只对打印感兴趣,而不对结果感兴趣。

此外,map 是惰性的,并且在使用 doall 打印或评估之前不会被评估。

【讨论】:

    【解决方案3】:

    在大多数情况下,您可以使用mapforloop

    => (map count citrus-list)
    (5 6 10)
    
    => (for [c citrus-list] (count c))
    (5 6 10)
    
    => (loop [[c & citrus] citrus-list
               counts []]
         (if-not c counts
           (recur citrus (conj counts (count c)))))
    [5 6 10]
    

    我倾向于尽可能使用map。语法更简洁,它清楚地将控制流(顺序循环)与转换逻辑(计算值)分开。

    例如,您可以通过简单地将map 替换为pmap 来并行运行相同的操作(计数)

    => (pmap count citrus-list)
    [5 6 10]
    

    在 Clojure 中,most operations on collection are lazy。只要您的程序不需要新值,它们就不会生效。要立即应用效果,您可以将循环操作包含在 doall

    => (doall (map count citrus-list))
    (5 6 10)
    

    如果您不关心返回值,也可以使用doseq。例如,您可以将doseqprintln 一起使用,因为该函数将始终返回nil

    => (doseq [c citrus-list] (println c))
    lemon
    orange
    grapefruit
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-06
      • 1970-01-01
      相关资源
      最近更新 更多