前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python和Scala的一等函数

Python和Scala的一等函数

作者头像
哒呵呵
发布2018-08-06 14:14:04
5850
发布2018-08-06 14:14:04
举报
文章被收录于专栏:鸿的学习笔记鸿的学习笔记

函数指的是执行某个任务或者是一系列的指令被组织成的一片代码块。标准的数学意义上的函数指的是输入集合和输出集合的一种对应关系。

函数 而在Scala和Python里,函数是一等对象,这个得益于它们对于函数的实现都是基于类的函数实例。也就是说,函数本身就是一个对象。Scala的函数都是FunctionN包一个特质的类的实例,例如Function0代表不带参数的函数,Function1代表带一个参数的函数,使用apply方法调用函数。

代码语言:javascript
复制
scala> def sum(a:Int) = a
sum: (a: Int)Int

scala> val a = sum _
a: Int => Int = <function1>

scala> a.apply(1)
res1: Int = 1

scala> a.
andThen        apply          asInstanceOf   compose        isInstanceOf   toString

而Python的函数则是function类的实例。

代码语言:javascript
复制
>>> def sum(a):
...   return a
...
>>> type(sum)
<class '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里,这么写:

代码语言:javascript
复制
scala> val Numbers = List(0,1,2)
Numbers: List[Int] = List(0, 1, 2)

scala> def equalsZero(x:Int) = {x == 0}
equalsZero: (x: Int)Boolean

scala> Numbers.filter(equalsZero)
res2: List[Int] = List(0)

我们可以把equalsZero作为一个参数传入filter方法里选出列表里等于0的数,生成一个新的列表。同样的Python也是可以实现的:

代码语言:javascript
复制
>>> def equals_zero(x):
...   return x == 0
...
>>> numbers = [0,1,2]
>>> list(filter(equals_zero,numbers))
[0]

因为在Python3里大量使用了生成器,所以我们需要使用list内置函数将filter函数返回的值求出来。

2.匿名函数 严格来说,匿名函数是没有绑定标识符的函数定义,“a function definition that is not bound to an identifier.”。它起源于λ表达式,可以算是函数式编程里面一个很核心的概念了。在Scala里,匿名函数被称为函数字面量,是用圆括号括起来的一组带名字的参数,一个右箭头和函数体。它会在运行时实例化为正常的函数实例。

代码语言:javascript
复制
scala> val add = (x:Int, y:Int) => x+y
add: (Int, Int) => Int = <function2>

scala> add(1,2)
res1: Int = 3

函数字面量也同样的支持类型推断,例如在之前的Numbers.filter可以这么写:

代码语言:javascript
复制
scala> Numbers.filter((x) => x == 0)
res3: List[Int] = List(0)

Scala的编译器知道x必定是整数,因为你是用来过滤一个整数组成的列表,所以Scala可以推断出x是Int类型。进一步我们也可以把括号去掉。

代码语言:javascript
复制
scala> Numbers.filter(x => x == 0)
res4: List[Int] = List(0)

Scala为了更加简化函数字面量,还引入了下画线_作为占位符,用来表示一个或者多个参数。,只要满足每个参数只在函数字面量出现一次就好了。例如:

代码语言:javascript
复制
scala> Numbers.filter(_ == 0)
res5: List[Int] = List(0)

可以这么理解,作为一个“空”,入参被“填”进去了这个“空”。每一次列表的循环, == 0变成了 0 == 0, 1 == 0,2 == 0直到列表结尾。Scala的占位符还做了进一步的引申:

代码语言:javascript
复制
scala> val equalsZero = (_:Int) == 0
filter: Int => Boolean = <function1>

scala> equalsZero(0)
res2: Boolean = true

scala> equalsZero(-1)
res3: Boolean = false

不同于Scala给匿名函数这么多的支持,Python相对来说不怎么建议你使用匿名函数的。Python使用lambda关键字创建匿名函数:

代码语言:javascript
复制
equals_zero = lambda x :x == 0

但是你只能使用纯表达式,不能使用更复杂的赋值和控制结构。Python认为lambda表达式会导致代码的难以阅读,违背了Python易读的特性,所以给匿名函数施加了极大的限制。

在工程中应该尽量避免匿名函数,除非你确认别人可以不依靠函数名就知道你函数的意义(然而这是很难的),虽然Scala给匿名函数提供了这么多的方便,极大地减少你的手指劳累,我依然不建议你使用。

3.部分应用函数 在函数式编程里还有一个核心的概念,就是部分应用函数,它是基于一个已创建的函数,把原函数的某些参数进行了冻结,只接受一部分的参数。 在Scala的实现里,使用了_占位符:

代码语言:javascript
复制
scala> def sum(a:Int,b:Int) = a + b
sum: (a: Int, b: Int)Int

scala> val a = sum(_:Int,2)
a: Int => Int = <function1>

scala> a(1)
res6: Int = 3

而Python则需要内置库的functools模块的partial。

代码语言:javascript
复制
>>> def sum(a,b):
...   return a + b
...
>>> from functools import partial
>>> a = partial(sum,2)
>>> a(3)
5
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 鸿的学习笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档