Clojure中的Map惰性求值

6
我正在用Clojure构建简单的Swing GUI。我试图通过在let上下文中使用map将单个函数应用于多个GUI组件:
`(map #(f % component4) [component1 component2 component3])`
其中所有组件都在let中定义。
问题是,map是延迟的,操作没有应用到组件上,但我可以通过在上述代码中包装一个“take”来强制执行它。
是否有非惰性的map替代方案?或者我应该用不同的方法处理?
编辑:在eclipse中使用counterclockwise。从REPL中使用(use 'Lib :reload)和从编辑器中使用CTRL + Enter获得了不同的结果。重新加载将启动GUI,但会出现上述描述的问题。当从编辑器中使用CTRL + Enter时,问题没有发生,因此我认为我对问题的描述可能不准确。无论如何,在这种情况下,doseq似乎是map的更好选择。
3个回答

17

我质疑你所说的加入take是否有任何不同。如果你用doalldorun将其包装起来,它将完成你想要的,但是你应该考虑使用doseq而不是map来处理这种只有副作用的操作。

注意

最初是作为评论发布的,因为受到广泛欢迎而被复制到答案中。


我不明白。如果你取走所有元素,难道不是强制实现它们吗?我并不是在争论doall等方法是否更好,但是假设take 3也可以工作,对吧? - Adrian Mouat
啊!我从未知道,但事实上: (first (take 10000 (map #(do(Thread / sleep 10)%)(range))))) 非常快地返回。 - Adrian Mouat
@AdrianMouat 实际上它实现了32个元素,因为range和map都是为了效率而分块的 - 所以它只需要0.32秒。但是(first (take 10000 (map #(do (Thread/sleep 10) %) (repeat 1))))像预期的那样在0.01秒内返回。 - amalloy
我确实想知道为什么使用更高的睡眠值时它会在tryclj.com上超时。谢谢,今天早上我学到了很多 :) - Adrian Mouat

8

doseq 可能是处理这个问题的最佳方式。在其他许多语言中,doseq 大致相当于“for-each”语句,可以循环遍历集合中的每个元素。它保证是非惰性的。

(doseq 
  [comp [component1 component2 component3]]
  (f comp component4))

一些常见建议:

  • 当需要一个序列作为输出时,请使用map及其惰性朋友(包括take,drop等)
  • 当您更关注副作用时,请使用doseqdoalldotimes

1
经验法则:副作用?寻找名称中的“do”。 - kotarak

2
将您的地图包装在doall中将强制对其进行评估。更好的替代方法是使用doseq,它用于涉及副作用的事情。

谢谢,这个很好用。语法也更适合我要做的事情。 - user1064799

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接