18

在尝试保持功能风格时,我很难理解何时应该更喜欢:

(-> [1 2 3] reverse last)

超过:

(last (reverse [1 2 3]))

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

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

4

5 回答 5

14

我大多避免使用->单参数函数;当您拥有多参数函数时,它会更有用,因为它可以让您将每个函数保持在其“额外参数”旁边,而不会被焦点对象遮挡。

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

(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 的论点是它节省了两个打字字符,这根本不是一个论点。

于 2012-10-03T20:51:50.213 回答
9

我的简短经验法则是“哪个更接近于更具陈述性的英语句子”。如果您将它们粗略地翻译成描述性的句子,我倾向于选择更准确地说明正在产生的事物的句子,而不是描述创建结果的过程的选项。有些人会想选择另一种方式。这些东西总是口味问题。

例如:

  • (last (reverse [1 2 3]))几乎是“最后倒车 1 2 3”
  • (-> [1 2 3] reverse last)有点像“从 1 2 3,反向,最后”

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

于 2012-10-03T20:15:52.600 回答
8

由于它们在功能上是相同的,所以要考虑的主要问题是哪个使代码更具可读性和可维护性?

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

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

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

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

  • 它通常是一种更自然的数学运算/计算风格,因为它更接近于数学函数符号
  • 它更灵活,因为它不要求传递的值必须始终是第一个 (with ->) 或最后一个 (with ->>) 参数
  • 它没有想象额外的论点的精神开销
于 2012-10-04T04:09:57.767 回答
3

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

于 2012-10-03T21:07:11.680 回答
3

其他答案没有错,但我只想补充一点,->对我来说真正有意义的时候是你想强调应用了一系列操作。从左到右阅读意味着您自然会看到计算函数的顺序,一旦您超过 2 或 3 层嵌套,这真的很有帮助。使用长手形式,您有时必须在脑海中解析括号,深入到最低级别,然后返回以了解正在发生的事情。

于 2012-10-04T02:48:01.007 回答