Clojure宏中map exec、for循环、do及~@body的差异与安全性问询
关于Clojure宏
with-cwd的几个问题解答 1. (map exec ~body)与(for [node body] node)的差异
两者核心行为完全不同:
(map exec ~body):会把exec函数逐一应用到~body的每个元素上,生成由exec返回值组成的惰性序列。如果exec是带副作用的操作(比如执行系统命令),由于map的惰性特性,这些副作用不会立即触发,只有当序列被主动消费(比如用doall/dorun遍历、打印序列)时才会执行。(for [node body] node):这里的body未加反引号展开(~),宏展开后实际是遍历符号body本身而非传入的代码体内容,完全不会调用exec,只是生成一个无意义的惰性序列。就算修正为(for [node ~body] node),也只是原样返回~body的元素序列,同样不会执行exec,根本达不到执行代码体的目的。
2. 安全性对比及Python 3 exec变化的影响
for的写法完全无法实现预期功能(没执行exec),根本谈不上安全与否;而(map exec ~body)存在严重逻辑问题:由于惰性,exec的执行时机可能滞后于finally块的目录恢复操作——也就是说,exec可能会在切换回原工作目录后才执行,完全违背了with-cwd临时切换目录的初衷,所以这种写法也不可用。
另外,Python 3中exec()的变化和这个Clojure宏完全无关,两者是不同语言的功能,不存在影响关系。
3. (do ~@body)与直接~@body的区别
在Clojure的try块语境下,两者执行效果几乎一致:
- 直接
~@body:会把body中的所有表达式直接展开到try块内,try块本身支持多个表达式按顺序执行,返回最后一个表达式的结果。 (do ~@body):把body的表达式包裹在do里再放入try块,do本身也是按顺序执行多个表达式并返回最后一个的结果。
唯一细微差异是当body为空时:直接~@body会让try块内没有表达式,try返回nil;(do ~@body)则是执行空的do,同样返回nil,实际效果无区别。
如果追求简洁写法,直接用~@body即可,无需额外的do包裹,也不用命名多余变量。
内容的提问来源于stack exchange,提问作者syvlorg
相关产品推荐
相关产品推荐

