函数指的是执行某个任务或者是一系列的指令被组织成的一片代码块。标准的数学意义上的函数指的是输入集合和输出集合的一种对应关系。
函数
而在Scala和Python里,函数是一等对象,这个得益于它们对于函数的实现都是基于类的函数实例。也就是说,函数本身就是一个对象。Scala的函数都是FunctionN包一个特质的类的实例,例如Function0代表不带参数的函数,Function1代表带一个参数的函数,使用apply方法调用函数。
而Python的函数则是function类的实例。
作为一等函数,自然有着与众不同之处。在维基里,一等函数是“the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures.”翻译过来就是函数是可以赋值给变量或数据结构中的元素,可以作为参数传给函数,可以作为函数的返回结果。这篇文章先不讨论闭包的概念,专注于一等函数的这些性质是如何在Scala和Python里体现出来的。
1.高阶函数
高阶函数在维基的定义是“a function taking another function as argument”,一个函数可以接受函数作为参数。在函数式编程里最为出名的就是map,filter和reduce这三个高阶函数。例如我们可以在Scala里,这么写:
我们可以把equalsZero作为一个参数传入filter方法里选出列表里等于0的数,生成一个新的列表。同样的Python也是可以实现的:
因为在Python3里大量使用了生成器,所以我们需要使用list内置函数将filter函数返回的值求出来。
2.匿名函数
严格来说,匿名函数是没有绑定标识符的函数定义,“a function definition that is not bound to an identifier.”。它起源于λ表达式,可以算是函数式编程里面一个很核心的概念了。在Scala里,匿名函数被称为函数字面量,是用圆括号括起来的一组带名字的参数,一个右箭头和函数体。它会在运行时实例化为正常的函数实例。
函数字面量也同样的支持类型推断,例如在之前的Numbers.filter可以这么写:
Scala的编译器知道x必定是整数,因为你是用来过滤一个整数组成的列表,所以Scala可以推断出x是Int类型。进一步我们也可以把括号去掉。
Scala为了更加简化函数字面量,还引入了下画线_作为占位符,用来表示一个或者多个参数。,只要满足每个参数只在函数字面量出现一次就好了。例如:
可以这么理解,作为一个“空”,入参被“填”进去了这个“空”。每一次列表的循环,== 0变成了 0 == 0, 1 == 0,2 == 0直到列表结尾。Scala的占位符还做了进一步的引申:
不同于Scala给匿名函数这么多的支持,Python相对来说不怎么建议你使用匿名函数的。Python使用lambda关键字创建匿名函数:
但是你只能使用纯表达式,不能使用更复杂的赋值和控制结构。Python认为lambda表达式会导致代码的难以阅读,违背了Python易读的特性,所以给匿名函数施加了极大的限制。
在工程中应该尽量避免匿名函数,除非你确认别人可以不依靠函数名就知道你函数的意义(然而这是很难的),虽然Scala给匿名函数提供了这么多的方便,极大地减少你的手指劳累,我依然不建议你使用。
3.部分应用函数
在函数式编程里还有一个核心的概念,就是部分应用函数,它是基于一个已创建的函数,把原函数的某些参数进行了冻结,只接受一部分的参数。
在Scala的实现里,使用了_占位符:
而Python则需要内置库的functools模块的partial。
领取专属 10元无门槛券
私享最新 技术干货