免责声明:这将有点保修。 我对听起来给我们带来的“问题”并不感到沮丧; 相反,它用来强调我的思想过程为何做到了这一点。 我什至不打算使用标题标题,这对我来说很奇怪:)
我最近有一个关于调用函数的思考过程。 功能语言(有时甚至是其他语言)如何实现某个目标一直令我感到沮丧。
该目标是链接电话。
我认为似乎没有人做对。 唯一有效的方法是使用对象的流畅接口。 让我们以一个简单的例子filter
荷兰国际集团的列表(称为myList
的偶数)(谓词函数将被调用evens
),然后map
平的结果squared
:
在具有用于filter
和map
泛型函数的典型多范例语言中,例如Python:
map(squared, filter(evens, myList))
或一种口齿不清的语言:
(map squared (filter evens myList))
首先让我想到的是,这些功能从右到左,由内而外运行。 对于那些经常从事数学工作的人来说,从内而外移动并不困难,但是我们仍然更喜欢从左到右(反正那些主要语言是从左到右的人)来做。 必须颠倒这些函数调用,这很麻烦,并且减慢了我们确定代码功能的能力。
注意:另外,请注意,这些函数将传入的函数放在参数列表中而不是列表中。 我个人一直认为最好将要处理的主要对象放在首位。 我可以想到的首先包含这些函数的唯一原因是,它通过使用currying或使用Python的partial
提供了更易于创建的高抽象函数(例如,使reduce
成为sum
函数)。 参数的顺序将在稍后的“解决方案”中再次出现。
许多这些语言提供的一个有用的功能是组合函数,它使我们可以将函数调用组合成一个函数。 如果我们分别将map
和filter
函数分别咖喱化或partial
map_squared
和filter_evens
,则可以像下面这样进行compose调用:
compose(map_squared, filter_evens, myList)
或者像这样一口气:
(compose map_squared filter_evens myList)
注意到什么了吗? 我们仍在从右到左阅读! 出于某种原因,人们普遍同意compose(f, g, data)
应该读为“ f
of g
”(像数学家一样),而不是“ f
then g
”(像其他任何人一样)。 当然,函数式编程(可能是其他编程的90%)是由对数学感兴趣的人员设计的。 哎呀,我很喜欢数学,曾经两次学过微积分(第一次是在高中,第二次是在大学,两次都是为了娱乐)。 但这仍然不是我喜欢的读写函数调用的方式。
我喜欢什么 如前所述,流畅的OO API可以很好地工作。 理想情况下,我想打电话给
myList.filter(evens).map(squared)
有人可能会认为这类似于Java 8的Stream
API的外观。 这也与Kotlin的外观非常接近。
但是,这是OO方法的问题:
- OO仅适用于OO语言(显然)
- 为了完成这些流畅的API,您要么必须先将它们构建到对象中,要么必须创建某种实现它们的包装器类。
- 即使您确实使用了流畅的API,以后仍可能会需要一些由于某些原因而无法添加的新功能。
使用扩展方法可以缓解最后两个问题(出于明显的原因,扩展方法仅包含在OO语言中),但是我决定从一种语言的角度出发,该语言要么是纯功能性的,要么至少是部分OO而不包含语言。创建扩展方法的方法。
我唯一的解决方案是为调用函数创建一种新的可选方法。 它部分地受到了Python方法的启发,这些方法已经做到了,但是相反。 它们允许像普通函数一样调用对象方法,而我的解决方案允许像对象方法那样调用普通函数。 有关Python如何执行前者的解释,请参阅本文,了解为何我认为self
争论是个天才 。
因此,如果您还没有弄清楚,我的解决方案是让函数的第一个参数像您要调用该函数(作为“方法”)的对象一样工作。 这样,您可以采用许多Python的内置函数,例如len
, iter
, next
等,并像这样调用它们:
myList.len()
myList.iter()
myIterator.next()
这更符合其他OO语言。
它们不是真正的方法,但是看起来像方法,它允许它们像流利的方法一样被链接起来。 回到前面的旁注中,我们必须颠倒filter和map(以及其他类似对象)的参数顺序,以使序列优先。 否则,我们的示例将无法正常工作,如下所示:
squared.map(evens.filter(myList))
哪一个甚至还没有开始解决问题。
那如何进行partial
使用? 好吧,您似乎必须手动定义sum
函数,而不是使用currying。 例如(假设reduce现在首先列出一个列表):
def sum(sequence):
return reduce(sequence, operator.add)
至于局部的,如果您使用关键字参数,它仍然可以工作:
sum = functools.partial(function=operator.add)
lisps呢? 他们没有OO调用语法。 这怎么可能与lisps一起工作? 简单:新方法可以将第一个参数放在第一位, 然后是被调用函数的名称,并以句点作为前缀。 例如,新的地图和过滤器语法如下所示:
((myList .filter evens) .map squared)
所以你怎么看? 你喜欢这个主意吗? 我很想开发一种具有这种功能的语言,但是我正忙于其他想法,以至于自己不愿意创建一种新语言。 如果当前使用的语言要在其功能表中添加这种语法糖,我会更喜欢它。 手指交叉。 因为我已经厌倦了为这类东西编写包装器类。
翻译自: https://www.javacodegeeks.com/2015/03/a-new-idea-for-calling-functions.html