函数是编程的闭包和迭代器理解

函数是编程的闭包和迭代器理解

什么是闭包?

闭包是一类特殊的函数。如果一个函数定义在另一个函数的作用域中,并且函数中引用了外部函数的局部变量,那么这个函数就是一个闭包。

1
2
3
4
5
6
7
8
9
10
11
12
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum

$ lazy_sum(1,2,3,4,5)
<function sum at 0x10452f668>
$ lazy_sum(1,2,3,4,5)()
15

在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,
当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,称为闭包

需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了 f() 才执行。我们来看一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def count():
fs = []
for i in range(1,4):
def f():
return i * 4
fs.append(f)
return fs
$ f1, f2, f3 = count()
$ f1()
9
$ f2()
9
$ f3()
9

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
因为她封装的是这个变量,最后求值的时候才会去取这个变量,上面i最后的值是3.
所以三个函数得到的都是9
如果非要在引用变量,可以这么做

1
2
3
4
5
6
7
8
9
def count():
fs = []
for i in range(1,4):
def wrap(i): #i参数传递进去是确定的值
def f():
return i * 4
return f
fs.append(wrap(i)) #已将i作为固定值传递进去
return fs

闭包有什么用处?

本质上,闭包是将函数内部和函数外部连接起来的桥梁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> def ExFunc(n):
sum=n
def InsFunc():
return sum+1
return InsFunc

>>> myFunc=ExFunc(10)
>>> myFunc()
11
>>> myAnotherFunc=ExFunc(20)
>>> myAnotherFunc()
21
>>> myFunc()
11
>>> myAnotherFunc()
21
>>>

由于闭包把函数和运行时的引用环境打包成为一个新的整体,所以就解决了函数编程中的嵌套所引发的问题。如上述代码段中,当每次调用ExFunc函数时都将返回一个新的闭包实例,这些实例之间是隔离的,分别包含调用时不同的引用环境现场。不同于函数,闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例

他最根本的用途是提供了更深的代码复用

怎么理解函数是一等公民?

变量是一等公民,变量怎么用,函数就能怎么用
变量出现的地方,函数都可以出现

怎么理解函数式编程的惰性求值?

函数式编程的主题之一是关注分离(separation of concerns)。希望将计算的描述与实际运行分离
一般而言,惰性化对一个表达式分离了它的描述和求值。

1
2
3
4
5
6
7
8
9
10
scala> def maybeTwice(b: Boolean, i: => int) =  if(b) i+i else 0
scala> val x = maybeTwice(true, { println("hi"); 42}) #{,x}的传参方法可以用于debug
hi
hi
x: Int = 84
scala> val x = maybeTwice(false, { println("hi"); 42})
x: Int = 0
# 可以表达式{println("hi"),2} 根本就没有被执行,它也不需要被执行,
# 但是如果是传值调用,那么b不管真否,表达式都必须求值
# 所以只有当用到时才求值,用不到不求值,也是传名调用的一种用途吧

反过来,如果这个参数被用到多次,那么传名参数要被求值多次,反而效率不如传值参数。

1
2
3
4
5
6
7
scala> def maybeTwice(b: Boolean, i: => int) = {
| lazy val j = i
| if(b) j+j else 0
| }
scala> val x = maybeTwice(true, { println("hi"); 42})
hi
x: Int = 84

惰性求值参考

如何理解lazy

1
2
3
4
5
6
7
8
9
10
scala> val x = { println("i'm positive"); "Howdy"}   //赋值时就积极执行expr求值 赋值给变量
i'm positive
x: String = Howdy
scala> println(x)
Howdy
scala> lazy val y = { println("i'm lazy"); "Vigilance"} //等用到y的时候在执行expr
y: String = <lazy>
scala> println(y)
i'm lazy
Vigilance

scala 的传名调用,传值调用

() => Unit 是函数
=> Unit 是执行结果为Unit的表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
object Add {
def addByName(a: Int, b: => Int) = a + b + b
def addByValue(a: Int, b: Int) = a + b + b
}

scala> Add.addByValue(2, {println("callByValue");3})
callByValue # b 的求值只被执行了一次,传值调用
res15: Int = 8

scala> Add.addByName(2, {println("callByName");3})
callByName # b 被执行了2次,传名调用
callByName
res17: Int = 8

到现在 你可以发现传名参数本质就是将参数的lazy化,
类似于 def addByName(a: Int, lazy b: Expr),只不过没有定义成这种语法而已

Chisel里的lazy module有什么用?

官方解释
The LazyModule construct was created as a way to delay calling the constructor until after there’s been a chance to calculate the parameters to give it. LazyModule decouples the point in the code where a circuit element is instantiated, from the point in the code where the constructor is called and that instance is actually built. In Rocket, one main driver of this lazy approach is Diplomacy), which creates the TileLink2 and AXI connections on the chip. Diplomacy, in turn, is built on top of the Cake pattern

怎么理解函数式编程无副作用?

评论