【Scheme】编程学习 (三) —— 闭包

简介: 本节主要讲述 Scheme 中闭包概念及使用

原视频地址
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))) ; 函数实际的函数体
AI 代码解读

函数内定义函数,有些类似 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));
}
AI 代码解读

二、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
AI 代码解读

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();
    }
}
AI 代码解读

在 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)
AI 代码解读

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() };
}
AI 代码解读

表示,scheme 函数内的函数也可以使用外层函数内部定义的符号

三、Returning functions

函数可以返回内部定义的函数

(define (make-add-one)
    (define (inc x) (+ 1 x); 定义过程 (inc x)
    inc); 返回 inc 过程
AI 代码解读

定义一个过程 make-add-one 不需要任何入参,在过程中定义一个函数,并且返回
inc 过程为返回入参+1的数值。

this is a function which returns a function,这是一个返回值为函数的函数

调用函数 make-add-one

> (make-add-one) 
; # <procedure:inc>
AI 代码解读

表示,返回一个 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
AI 代码解读

make-add-one doesn't take any argument
调用报错 make-add-one 过程期待无参数,但是给予了一个参数

正确的定义方法为,定义符号 myfn 为调用 make-add-one 的结果

(define myfn (make-add-one))

> (myfn 2)
; 此时调用结果为 3
AI 代码解读

四、Closures

为了理解闭包的概念,需要理解以下过程

(define (make-add-x x)
    (define (add-x y) (+ x y))
    add-x); 返回 add-x 过程
AI 代码解读

定义符号 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
AI 代码解读

这里发生了什么,调用 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)
AI 代码解读

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
AI 代码解读

调用一次产生一个闭包,

5.2 Uses - Testing

测试

(define (shout display-fn txt)
    (display-fn
        (list->string
            (map
                char-upase
                (string->list txt)))))
AI 代码解读

定义一个函数为 shout ,传入 display-fn 过程,和 参数 txt。
将输入的字符串,一个字符一个字符设置为大写。并将其作为参数给与 display-fn 过程。

传入正常的 display 和 "boo"

> (shout display "boo")
; BOO
AI 代码解读
(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"))
AI 代码解读

定义一个函数为 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)
AI 代码解读

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;    
    }
};
AI 代码解读
(define a (make-balance))

> (a "get")
;#<procedure:get-method> ; 只能返回一个过程 procedure
> ((a "get")) ; 需要结果只能使用调用过程
;0
> ((a "add") 3)
;  nothing happened
> ((a "get"))
; 3
AI 代码解读

重新定义一个 b

> (define b (make-balance))
> ((b "get"))
; 0
> ((b "add") -1)

> ((b "get"))
;-1
> ((a "get"))
; 3 仍然是 3
AI 代码解读
相关文章
对象OSS生命周期(LifeCycle)管理功能|学习笔记
快速学习对象 OSS 生命周期(LifeCycle)管理功能
2945 0
对象OSS生命周期(LifeCycle)管理功能|学习笔记
VMware ESXi 8.0U3c macOS Unlocker & OEM BIOS xFusion (超聚变) 定制版
VMware ESXi 8.0U3c macOS Unlocker & OEM BIOS xFusion (超聚变) 定制版
190 12
VMware ESXi 8.0U3c macOS Unlocker & OEM BIOS xFusion (超聚变) 定制版
百炼融合AnalyticDB,10分钟创建网站AI助手
百炼融合AnalyticDB,10分钟创建网站AI助手。本课程由阿里云产品经理陈茏久分享,涵盖大模型行业变革、向量数据库驱动RAG服务化探索、方案优势及应用场景、产品选型配置及最新发布等内容。通过整合通义百炼和AnalyticDB,用户可快速搭建具备企业私域知识的AI助手,实现智能客服、教育、汽车等多行业的应用升级。教程详细介绍了从环境搭建到知识库配置的全流程,并提供了免费试用资源,帮助用户低成本体验核心能力。
252 7
dockercompose如何配置特权启动
dockercompose如何配置特权启动
【Scheme】编程学习(一) —— 概述
Scheme 是一种编程语言,为 Lisp 的一种变体,本文概述 Scheme 语言
377 0
【Web 前端】如何操作DOM元素?
【4月更文挑战第22天】【Web 前端】如何操作DOM元素?
Linux运维工程师必须要掌握的Docker命令,我给你整理好了!
Linux运维工程师必须要掌握的Docker命令,我给你整理好了!
720 2
Java异常处理:Error和Exception有什么区别?
Java异常处理:Error和Exception有什么区别?
358 0
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问