2024年 Clojure 状态调查!中分享你的想法。

欢迎!请查看关于页面获取更多关于如何使用本网站的信息。

+4
Clojure

代码中有一些地方需要定义并仅在该处使用的函数。定义此类函数有2种主要方式,一种是无名函数
(map #(blepify-with blep (:blop %)) xs)
另一种是高阶函数
(map (comp (partial blepify-with blep) :blop) xs)

我个人不使用`partial`,总是更倾向于使用匿名函数,因为

  • 匿名函数在调用时会进行变量引用,这意味着我可以重定义`blepify-with`,并且新定义会被自动使用。这在开发过程中非常有帮助,而且与直接链接一样,在生产环境中也能顺畅运行;
  • `partial`通过隐藏函数参数数量使代码的可读性降低。明确比隐含好,Cursive可以立即显示我是否向函数传递了错误的参数数量

我也很少使用`comp`。除了关于变量引用相同的论点外,它还使我必须从右向左阅读代码。在小示例中这并不重要,但随着代码量的增长,线程宏大大提高了可读性。
唯一我觉得`comp`有用的时候是函数应用顺序与数据处理顺序相反,例如转换流(transducers):`(comp (filter even?) (map inc))`的意图是从左到右阅读。尽管`(map inc)`最先被调用,但经过该转换流处理的数据首先会通过`even?`谓词,然后通过`inc`转换。

我的问题是,`partial`和`comp`还有什么是我没注意到的吗?或许这仅仅是一个口味问题,我只是还没有习惯它们,也许它们同样具有可读性,而且不必重新定义使用位置的优点被高估了?我应该什么时候优先使用`comp`和`partial`而不是匿名函数?

2 答案

+7
by

在我看来,你应该优先选择匿名函数而不是偏函数和 comp,这是 Clojure 的惯用解决方案。

我唯一使用 comp 的时候是组合 transducer 链。我几乎从不使用 partial。

+3
by

我将 comppartial 重构为 fnletfn#() 表达式。结果可能更冗长,但更容易理解和调试。

调试的难度是编写代码的两倍。
因此,如果你尽可能地编写聪明代码,那么
根据定义,你不足以调试它。

— Kernighan 定律

我的偏好是避免过于聪明或过分追求代码简洁,以至于忽视了其他考虑。

by Dave Roberts
正如其他人所说的,匿名函数更为常见,但有时我也会使用 partial。我很少使用 comp,通常只是用于 transducer,因为它使得应用顺序更容易理解;否则,我使用 threading 宏。只有当应用极为直接,即我拥有函数应用除了最后一个参数之外的所有参数,且需要在高阶函数中使用这些参数时,我才会使用 partial。保持简单易懂。
...