Clojure套用与地图

Clojure apply vs map

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

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

打印:

1
2
{: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却不行。我在哪里傻?


您很有可能被map的懒惰打中。 (map产生一个惰性序列,只有在某些代码实际使用其元素时才实现。即使这样,实现也以块的形式发生,因此您必须遍历整个序列以确保全部实现。)尝试packagedorun

中的map表达式

1
(dorun (map println foundApps))

此外,由于您仅出于副作用而这样做,因此使用doseq可能更干净:

1
2
(doseq [fa foundApps]
  (println fa))

请注意,(map println foundApps)在REPL上应该可以正常工作;我假设您已经从代码中未强制提取的地方提取了它。 doseq没有严格的(即不偷懒的)差异,并且在任何情况下都会为您遍历其参数序列。另请注意,doseq返回nil作为其值;否则,返回nil。这仅对副作用有好处。最后,我从您的代码中跳过了rest;您可能已经说过(rest foundApps)(除非这只是一个错字)。

还请注意,(apply println foundApps)将在一行上打印所有foundApps,而(dorun (map println foundApps))将在其一行上打印foundApps的每个成员。


我对此帖子缺少一个简单的解释。让我们想象一个抽象函数F和一个向量。因此,

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

翻译为

1
(F 1 2 3 4 5)

这意味着F的首字母必须是可变的。

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

翻译为

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

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

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


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

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