Clojure:将doseq的绑定变量传递给另一个函数的问题

时间:2014-05-21 19:09:00

标签: clojure

我真的不确定这里有什么问题。我开始体验这个"问题"用这种代码:

首先,我确实用一些元数据定义了该字符串:

(def ^{:meta-attr ["foo" "bar"]
   :meta-attr2 "some value"} foo "some value")

然后我确实创建了以下两个函数:

(defn second-fn [values]
  (for [x values] (println x)))

(defn first-fn [value]
  (doseq [[meta-key meta-val] (seq (meta value))]
    (if (= meta-key :meta-attr)
      (second-fn meta-val))))

现在我在REPL中运行此命令:

(first-fn #'foo)

我得到了nil

但是,如果我更改second-fn

(defn second-fn [values]
  (println values))

如果我再次运行该命令,我会在REPL中找到它:

user> (first-fn #'foo)
[foo bar]
nil

我期望在我的函数的第一个版本的REPL中获得以下内容:

user> (first-fn #'foo)
foo
bar
nil

但是不知何故,我认为有些东西我不会通过doseq与绑定变量相关。

这是另一组具有完全相同行为的函数:

(defn test-2 [values]
;  (println values))
  (for [x values] (println x)))

(defn test-1 [values]
  (doseq [x values]
    (test-2 x)))

(test-1 [["1.1" "1.2"] ["2"] ["3"]])

我认为我缺少一些Clojure知识来了解这里发生了什么。当printlnpprint第二个函数中的值,但for不起作用时,为什么它看起来很好...

更新和最终想法

正如对这个问题的回答,问题与for函数的惰性有关。让我们用最简单的例子来说明发生了什么。

(defn test-2 [values]
  (for [x values] (println x)))

(defn test-1 [values]
  (doseq [x values]
    (test-2 x)))

test-1中会发生什么,每次doseq"迭代"时,都会创建一个新的非延迟序列。这意味着在"循环"期间,它们可以像任何其他集合一样访问。

当您使用可能有副作用的非纯函数时,通常应该使用

doseq,或者当您使用相对较小的集合时,我认为应该使用<{>}。{/ p>

然后,当test-2被调用时,for将创建 lazy-seq 。这意味着序列存在,但它从未实现(所以,每个步骤还没有被计算)。因为这两个函数都不会发生任何事情,因为for返回的值都没有实现。

如果我们要保留此doseq和此for个循环,那么我们必须确保在for中实现test-2。我们可以这样做:

(defn test-2 [values]
  (doall (for [x values] (println x))))

(defn test-1 [values]
  (doseq [x values]
    (test-2 x)))

这里doall的作用是强制完全实现for循环返回的序列。这样,我们将以预期的结果结束。

此外,我们可以使用其他函数来实现for返回的lazy-seq:

(defn test-2 [values]
  (first (for [x values] (println x))))

(defn test-2 [values]
  (count (for [x values] (println x))))

这些都没有意义,但所有这些例子都是为了实现for返回的lazy-seq。

此外,我们可以简单地使用两个doseq,如下所示:

(defn test-2 [values]
  (doseq [x values] (println x)))

(defn test-1 [values]
  (doseq [x values]
    (test-2 x)))

这样,我们就不会使用任何lazy-seq,所以我们不必实现任何事情,因为没有任何事情被评估为lazilly。

1 个答案:

答案 0 :(得分:2)

  • for很懒,而doseq很渴望。
  • for是&#34;功能性&#34; (值)和doseq是&#34;命令式的&#34; (副作用)。

换句话说,您不应该在for中使用second-fn,因为您似乎只担心副作用。你实际上在做什么是构建一个懒惰的序列(似乎从未执行过)。

有关详细信息,请参阅Difference between doseq and for in Clojure