在函数式编程中,以点无关的方式定义函数。有哪些优缺点?

5
每次当我写出这种形式的内容时:
let scorePopulation f population =
  Array.map (fun i -> f i) population

我开始思考,是不是写作会更好。
let scorePopulation f =
  Array.map (fun i -> f i)

相比第二种形式,使用第一种形式编写有什么优势吗?

此外,如何称呼第一种形式和第二种形式的函数定义?

6个回答

12

你甚至可以更进一步:

let scorePopulation = Array.map

就优缺点而言,我认为主要关注的是可读性。有时候,当所有参数都出现时,理解函数的作用会更容易。而其他时候,不需要编造多余的变量名的事实则更胜一筹。


2
在编程中,提及命名方式是很重要的。它能帮助我们更快地思考和编码。 - Daniel
2
+1。我的答案基本上是从不同的角度表达了相同的意思。名称很昂贵,不要把它们浪费在无关紧要的事情上,但是一定要给它们分配到相关的事物上。 - Jörg W Mittag

10

实际上,优点和缺点是相同的:命名。

原因是Phil Karlton所说的古话:

计算机科学中只有两个难题:缓存失效和命名事物。

如果命名很难(即代价高昂),那么就不应该把名称浪费在无关紧要的事物上。相反,如果某件事物有一个名称,则它很重要。

无参考样式使您可以省略名称。

优点是它允许您省略无关紧要的名称。

缺点是它允许您省略相关的名称。


7
我认为部分应用是函数式编程的一个优点。为什么不利用它呢?这会减少冗余。
另一件需要记住的事情是value restriction[MSDN]。没有参数的部分应用函数是一个函数值。真正的函数可以被泛化,但值不能。

6
你应该可以像第一个和第二个示例一样轻松调用它。这种技术通常被称为 point-free style. 它可能更好。它更短,所以默认情况下是一个加号。我同意上面链接中的警告:
随着高阶函数被链接在一起,推断表达式的类型可能变得更加困难。推断表达式类型的心理提示(显式函数参数和参数数量)会消失。
您也失去了给参数赋予有意义的名称的机会,这有时可能有助于理解。

5
个人而言,我讨厌无参风格,总觉得它难以阅读。正如其他人所提到的那样,
“你也失去了给参数赋予有意义的名称的机会,这有时可能有助于理解。”
总的来说,如果我在左侧定义命名函数,通常希望列出所有“预期”的参数。(相比之下,partial application非常适合匿名调用站点和本地lambda,但在撰写顶层代码时,我更喜欢更明确/记录的方式——作用域越大,软件工程方面的考虑就越多。)正如其他人所提到的,“值限制”会在从通用函数中删除所有参数时启动,这是一个技术限制,也微不足道地抑制了F#中的无参风格。

2
如果Intellisense在签名中包含参数名称,那么点式风格会更容易接受。您可以将鼠标悬停在函数上,然后查看(arg1:int -> arg2:int -> int) - Daniel
当你在 let h = if someCond then f else g 上悬停在 h 上面时,会得到什么?其中 fg 是具有相同类型但参数名称不同的函数。(更一般地说,参数名称在现有类型系统中没有任何有意义的“流动”方式,因此即使您克服了我在上一个句子中提出的设计问题,实现这一点也存在技术挑战。) - Brian
2
在那种情况下,回退到当前的方法?对于简单和部分应用函数(占大多数的情况下),这是否可能?它感觉像是一种泛化形式。在你的例子中,最具体的签名仅包含类型。 - Daniel

2

我经常提供参数,即使我可以省略它以增加清晰度。当我省略参数时,通常是因为函数体立即表明:i)有一个被省略的参数和ii)参数的性质是什么。例如,我发现只写下面这行代码就足够了:

let f = function [] -> "empty" | x::_ -> "not empty"

替代

let f l = match l with [] -> "empty" | x::_ -> "not empty"

一个更有趣的例子是编写:
let f = List.map g |> List.fold_left h

替代

let f l = List.map g (List.fold_left h l)

我认为第一个更清晰明了。这里的好处在于具有直观的高阶操作符,例如在Batteries中提供的|>


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