Clojure适用于地图

时间:2010-02-22 14:39:25

标签: clojure

我有一个从函数返回的序列(foundApps),我想将一个函数映射到它的所有元素。出于某种原因,applycount适用于sequnece但map不适用:

(apply println foundApps)
(map println rest foundApps)
(map (fn [app] (println app)) foundApps)
(println (str "Found " (count foundApps) " apps to delete"))))

打印:

{:description another descr, :title apptwo, :owner jim, :appstoreid 1235, :kind App, :key #<Key App(2)>} {:description another descr, :title apptwo, :owner jim, :appstoreid 1235, :kind App, :key #<Key App(4)>}
Found 2 apps to delete for id 1235

所以apply似乎很乐意为序列工作,但map却没有。我在哪里傻了?

3 个答案:

答案 0 :(得分:32)

很可能你被map的懒惰所打击。 (map生成一个惰性序列,只有在某些代码实际使用其元素时才会实现。即使这样,实现也会以块的形式发生,因此您必须遍历整个序列以确保它们都已实现。)尝试将map表达式包装在dorun

(dorun (map println foundApps))

此外,由于您只是为副作用而这样做,因此使用doseq可能更清晰:

(doseq [fa foundApps]
  (println fa))

请注意,(map println foundApps)应该在REPL中正常工作;我假设你已经从代码中的某个地方提取它,而不是强制它。 doseq与严格(即不是懒惰)没有任何区别,并且在任何情况下都会为你的论证序列行走。另请注意,doseq返回nil作为其值;它只对副作用有益。最后,我从您的代码中跳过了rest;你可能意味着(rest foundApps)(除非它只是一个错字)。

另请注意,(apply println foundApps)会在一行打印所有foundApps,而(dorun (map println foundApps))会在自己的行上打印foundApps的每个成员。

答案 1 :(得分:20)

我有一个简单的解释,这篇文章缺乏。让我们想象一个抽象函数F和一个向量。所以,

(apply F [1 2 3 4 5])

转换为

(F 1 2 3 4 5)

这意味着F必须是最好的变量。

虽然

(map F [1 2 3 4 5])

转换为

[(F 1) (F 2) (F 3) (F 4) (F 5)]

这意味着F必须是单变量的,或至少以这种方式表现。

关于类型有一些细微差别,因为map实际上返回了一个惰性序列而不是向量。但为了简单起见,我希望它是可以原谅的。

答案 2 :(得分:8)

一点解释可能会有所帮助。通常,您使用apply将一系列元素splat到一组函数的参数中。因此,将函数应用于某些参数只意味着将它们作为参数传递给函数,在单个函数调用中。

map函数将执行您想要的操作,通过将输入的每个元素插入函数然后存储输出来创建新的seq。它虽然懒惰,但是只有在实际迭代列表时才会计算值。要强制执行此操作,您可以使用(doall my-seq)函数,但大多数情况下您不需要这样做。

如果您需要立即执行操作,因为它有副作用,例如打印或保存到数据库或其他东西,那么您通常使用doseq。

所以要将“foo”附加到你的所有应用程序(假设它们是字符串):

  

(map(fn [app](str app“foo”))found-apps)

或使用shorhand进行匿名函数:

  

(map#(str%“foo”)found-apps)

执行相同操作但立即打印可以使用以下任一方法完成:

  

(doall(map#(println%)found-apps))

     

(doseq [app found-apps](println app))