我什么时候应该使用clojure箭头宏?

时间:2012-10-03 19:35:20

标签: syntax clojure macros operators

在努力保持功能风格时,我很难理解何时应该选择:

(-> [1 2 3] reverse last)

在:

(last (reverse [1 2 3]))

当我在一个项目中遇到这两种风格时,我发现它打破了我的流程,因为我必须在思考函数组合和思考中间值状态之间切换。

我应该在什么时候使用哪个?

5 个答案:

答案 0 :(得分:15)

我主要避免将->用于单参数函数;当你有多个参数函数时它会更有用,因为它允许你将每个函数保持在它的“额外参数”旁边,而焦点对象不会遮挡它。

但是,你也不必选择一个或另一个极端:我最喜欢的->之一是它允许你以任何顺序编写函数,你可以使用它以您认为最具可读性的方式自由编写代码。例如,也许你想要强调你正在采用最后一个集合,并且在上下文中它首先被颠倒的事实是无趣的:

(last (-> [1 2 3] (reverse)))

或许逆转很重要,最后很无聊:

(-> (reverse [1 2 3]) (last))

顺便说一句,请注意我在这里写了(last)(reverse)而不只是lastreverse,即使->宏隐式插入括号你离开了吗?这是有目的的:虽然它不是一种非常流行的风格,但我认为总是在给->的表格中使用括号是个好主意。这有几个原因:

  • 这意味着当(-> (blah) (fn [x] (+ 1 (* x 5))))奇怪地扩展时你不会感到惊讶,因为你习惯于认为->采用的是形式而不是函数。
  • 它保持“这里是一个开括号”和“正在调用一个函数”之间的链接。很高兴能够grep调用特定函数,这也是一个很好的视觉提示。如果省略括号,则在不进行视觉信号的情况下调用函数。
  • 在具有一些一元函数和其他多参数函数的多行->中看起来更规则。例如:

    (-> attrs
        (update :sessions inc)
        frobnicate
        save-to-disk
        (get :uuid))
    

    将这两件事放在中间偏移,看起来与其他一切不同,这不是很严重吗?

我见过的唯一一个省略() s的论点是它保存了两个字符的输入,这根本不是一个参数。

答案 1 :(得分:9)

我的简单经验法则是“哪一个更接近更具声明性的英语句子”。如果你粗略地将它们翻译成描述性句子,我倾向于选择更接近陈述事物的那个通过描述创建结果的过程的选项生成。有些人会想要选择其他方式。这些东西总是一个品味问题。

例如:

  • (last (reverse [1 2 3]))几乎是“最后一次来自逆转1 2 3”
  • (-> [1 2 3] reverse last)有点像“从1 2 3,反向,拿到最后”

我发现第一个更具说明性,在这种情况下会选择它,其他人会选择不同的。

答案 2 :(得分:8)

由于它们在功能上是相同的,因此要考虑的主要因素是,这使代码更具可读性和可维护性?

这基本上是一个判断电话。

->在以下情况下通常很有用:

  • 您希望明确表示您要按顺序执行一系列操作
  • 您可能希望将来插入新步骤。简单 - 只需添加一个新的行/表单。
  • 你的步骤有一些额外的参数(这使得很明显这些参数是步骤的一部分)
  • 深度嵌套的函数调用否则会让人感到困惑(尽管如果这确实是一个问题,还有其他方法可以重构......)

(f (g x))样式在其他情况下很有用:

  • 它通常是数学运算/计算的更自然的风格,因为它更接近于数学函数符号
  • 它更灵活,因为它不要求传递的值必须始终是第一个(使用->)或最后一个(使用->>)参数
  • 没有想象额外参数
  • 的精神开销

答案 3 :(得分:3)

我同意那些已经回答的人,但我会补充一点,如果你和很多使用C的人一起工作,后一种格式对他们来说可能更容易辨认,而如果你经常使用对于C#gurus,他们会更熟悉以前的格式与扩展方法。

答案 4 :(得分:3)

其他答案没有错,但我只想补充一点,->对我来说真的有意义的时候,你想要强调的是,正在应用一系列操作。从左到右阅读意味着你自然会看到评估函数的顺序,一旦你达到2或3级嵌套,这真的有帮助。使用长手形式,您有时需要在心理上解析括号,向下钻取到最低级别,然后按照自己的方式返回以了解正在发生的事情。