原视频地址
https://www.bilibili.com/video/BV1Kt411R7Wf?p=3
本节主要内容关于 Closures (闭包)
- Functions in functions 在函数中使用函数
- Using outer symbols 使用外部的符号
- Returning functions 如何返回一个函数
- Closures 什么是闭包
- Uses 闭包的使用
- Discussion 讨论
一、Functions in functions
(define (sum-of-squares x y)
(define (square a)
(* a a))
(define (sum b c)
(+ b c))
(sum (square x) (square y))) ; 函数实际的函数体
函数内定义函数,有些类似 C++ 函数体内的函数对象, lambda 函数。
double SumOfSquare(double x, double y)
{
auto square = [](double a)-> double
{
return a * a; };
auto sum = [](double b, double c)->double
{
return b + c; };
return sum(square(x), square(y));
}
二、Using outer symbols
使用外部符号
(define (assert-equal a b)
(define (print-error)
(display a)
(display " is not equal to ")
(display b)
(newline))
(if (not (equal? a b)) (print-error) null))
(assert-equal 3 (+ 1 2))
; does nothing
(assert-equal 3 (+ 2 2))
; print 3 is not equal to 4
display 函数用于打印到控制台 (print to console)
内部的函数 print-error 可以访问变量 a 和 b ,有些类似 lambda 函数对象的父域内容捕获
equal? 用于比较 a 和 b 是否相等 (evaluating if a is equal to b)
示例 C++ 代码:
// 函数对象名称 = [捕获](入参) {};
void AssertEqual(int a, int b)
{
// 以值的方式捕获父域中的 a, b
auto print_error = [a,b]()
{
std::cout << a << " is not equal to " << b << std::endl;
};
if (!(a == b))
{
print_error();
}
}
在 scheme 函数中定义函数,定义的函数可以访问外部函数中的变量。 C++ 中需要手动指定。
来看第二个例子
(define (circle-details r)
(define pi 3.14)
(define (area) (round (* pi r r)))
(define (circum) (round (* 2 pi r)))
(list (area) (circum)))
(circle-details 3)
; (28.0 19.0)
area 面积为 pi * r 的平方
round 表示获取近似值,四舍五入
如果传入圆半径 (radius) 会计算并返回圆的面积和圆的周长。
示例 C++ 代码
#include <cmath> // for round function
std::vector<double> CircleDetails(double r)
{
const double pi = 3.14;
auto area = [pi,r]() -> double
{
return round(pi * r * r); }
auto circum = [pi,r]() -> double
{
return round(2 * pi * r); }
return std::vector<double> {
area(), circum() };
}
表示,scheme 函数内的函数也可以使用外层函数内部定义的符号
三、Returning functions
函数可以返回内部定义的函数
(define (make-add-one)
(define (inc x) (+ 1 x); 定义过程 (inc x)
inc); 返回 inc 过程
定义一个过程 make-add-one 不需要任何入参,在过程中定义一个函数,并且返回
inc 过程为返回入参+1的数值。
this is a function which returns a function,这是一个返回值为函数的函数
调用函数 make-add-one
> (make-add-one)
; # <procedure:inc>
表示,返回一个 function 为 inc。
定义一个符号 myfn 为 make-add-one
(define myfn make-add-one) ; 定义一个符号为 make-add-one
>(myfn 2)
; procedure make-add-one: expects no arguments, give 1
make-add-one doesn't take any argument
调用报错 make-add-one 过程期待无参数,但是给予了一个参数
正确的定义方法为,定义符号 myfn 为调用 make-add-one 的结果
(define myfn (make-add-one))
> (myfn 2)
; 此时调用结果为 3
四、Closures
为了理解闭包的概念,需要理解以下过程
(define (make-add-x x)
(define (add-x y) (+ x y))
add-x); 返回 add-x 过程
定义符号 add-3 为调用过程 (make-add-x 3)的结果
(define add-3 (make-add-x 3))
> add-3
;<procedure:add-x>
> (add-3 4)
; call add-3 with argument 4, the answer is 7
; 调用 add-3 给与实参 4, 结果为 7
这里发生了什么,调用 make-add-x 使用的实参 3, 出于某种原因,被封锁在了 add-3 内部,
the name "closure" means that is not legal to do this kind of thing we just talk about,
closure is a concept that a system is closed in the sense that nothing was allowed to do in it.
“闭包”这个名字意味着做我们刚刚谈论的这种事情是不合法的,
闭包是一个概念,即一个系统是封闭的,从某种意义上说,它不允许在其中做任何事情。
you pass un argument 3, and you use it later. and it's still there
传入一个参数,之后使用,它仍在那里
五、Uses
5.1 Uses - Holding state
使用 - 保持状态
(define (make-counter)
(define value 0); 定义一个 value, 设置为 0
(define (counter); 定义一个无参函数 counter
(set! value (+ value 1))
value)
counter)
set! 是一个函数用于将 value 设置为 value + 1
定义 counter 函数,改变 value 的值,并返回 value。
外部的过程用于返回 counter 过程。
(define mycounter1 (make-counter))
> (mycounter1); it set value+1 and return value of 'value'
; 调用会设置 value 为 value + 1 并返回 value 的值
; 1
> (mycounter1)
; 2
> (mycounter1)
; 3
(define mycounter2 (make-counter))
> (mycounter2)
; 1
> (mycounter1)
; 4 ;结果是独立的
> (mycounter2)
; 2
> (mycounter1)
; 5
调用一次产生一个闭包,
5.2 Uses - Testing
测试
(define (shout display-fn txt)
(display-fn
(list->string
(map
char-upase
(string->list txt)))))
定义一个函数为 shout ,传入 display-fn 过程,和 参数 txt。
将输入的字符串,一个字符一个字符设置为大写。并将其作为参数给与 display-fn 过程。
传入正常的 display 和 "boo"
> (shout display "boo")
; BOO
(define (test-shout-displays-upper-case)
(define displayed "") ; display symbol make it empty string
(define (fake-display txt)
(set! displayed txt)); set symbol displayed to value txt
(shout fake-display "Hello Andy")
(assert-equal displayed "HELLO ANDY"))
定义一个函数为 test-shout-display-upper-case ,在内部定义一个符号 displayed 为空字符串,用于存储需要显示的字符,然后定义一个函数为 fake-display,入参为 txt,将符号 displayed 内容设置为 txt
assert-equal 用于检测两个字符串是否相同
5.3 Uses - Classes
使用,用于模拟 类,
(define (make-balance)
(define value 0)
(define (bal method)
(define (add-method x)
(set! value (+ value x)))
(define (get-method) value)
(if (equal? method "add")
add-method
get-method))
bal)
make-balance 可以视为 类的构造函数
class MakeBalance
{
int value;
int AddMethod(int x) {
value = value + x; }
int GetMethod() {
return value; }
public:
/*...*/ bal(std::string method)
{
if (method == "add")
return &AddMethod;
else
return &GetMethod;
}
};
(define a (make-balance))
> (a "get")
;#<procedure:get-method> ; 只能返回一个过程 procedure
> ((a "get")) ; 需要结果只能使用调用过程
;0
> ((a "add") 3)
; nothing happened
> ((a "get"))
; 3
重新定义一个 b
> (define b (make-balance))
> ((b "get"))
; 0
> ((b "add") -1)
> ((b "get"))
;-1
> ((a "get"))
; 3 仍然是 3