惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

K
Kaspersky official blog
T
Threat Research - Cisco Blogs
N
News and Events Feed by Topic
Hacker News: Ask HN
Hacker News: Ask HN
Project Zero
Project Zero
Application and Cybersecurity Blog
Application and Cybersecurity Blog
博客园 - 叶小钗
Security Latest
Security Latest
Spread Privacy
Spread Privacy
aimingoo的专栏
aimingoo的专栏
N
News and Events Feed by Topic
Webroot Blog
Webroot Blog
U
Unit 42
Cyberwarzone
Cyberwarzone
小众软件
小众软件
Scott Helme
Scott Helme
Engineering at Meta
Engineering at Meta
Microsoft Security Blog
Microsoft Security Blog
T
The Blog of Author Tim Ferriss
A
About on SuperTechFans
爱范儿
爱范儿
S
Schneier on Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Schneier on Security
Schneier on Security
Latest news
Latest news
GbyAI
GbyAI
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
The Register - Security
The Register - Security
WordPress大学
WordPress大学
博客园_首页
Blog — PlanetScale
Blog — PlanetScale
PCI Perspectives
PCI Perspectives
Jina AI
Jina AI
AI
AI
NISL@THU
NISL@THU
I
Intezer
G
GRAHAM CLULEY
B
Blog
S
Secure Thoughts
IT之家
IT之家
宝玉的分享
宝玉的分享
Recent Announcements
Recent Announcements
Y
Y Combinator Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell
有赞技术团队
有赞技术团队
V2EX - 技术
V2EX - 技术
Recorded Future
Recorded Future
Hacker News - Newest:
Hacker News - Newest: "LLM"

Qubik的小站

Building an Interpreter: Introduction and Lexer 在 Rocky Linux 10 Workstation KDE 上安装中文输入法 2025 上半年记 | Qubik的小站 Rust 写脚本?可以的!(上) | Qubik的小站 2024 年终总结 | Qubik的小站 Racket 入门 - 条件表达式、递归与复合数据 | Qubik的小站 Racket 入门 - 配置与基础 | Qubik的小站 2024 年上半年总结 | Qubik的小站 Java 中的 Lambda 表达式 | Qubik的小站 大 O 表示法 | Qubik的小站 递归与归并排序 | Qubik的小站 基础的排序与搜索算法 | Qubik的小站 Java 中的泛型 | Qubik的小站 数据结构:链表(LinkedList) | Qubik的小站 数据结构:栈与队列 | Qubik的小站
Racket 入门 - 模块与测试 | Qubik的小站
Qubik65536 · 2024-07-24 · via Qubik的小站

本文我将简单介绍一下 Racket 的模块以及如何进行测试。

模块 ​

我们在开发程序的时候,通常会讲代码分成多个文件以更好的组织代码,提高代码的可维护性。与几乎所有其他语言一样,Racket 同样支持将代码分成多个文件。但对于熟悉 Java、Python 等语言的读者来说,Racket 的模块系统可能稍微有些不同:因为你不只需要进行“导入”,还需要进行“导出”。

我们暂且想象有一个文件 utils.rkt,其中定义了我们程序所需要的一些工具函数。例如:

scheme

#lang racket

(define hello "Hello, World!")
(define num1 3)
(define num2 4.2)

(define (cube x) (* x x x))

(define (sum-of-cube x y)
  (+ (cube x)
     (cube y)))

而我们希望在我们的主程序文件 main.rkt 中使用 hello, num1, num2 三个变量以 sum-of-cube 函数。我们首先需要从 utils.rkt 中“导出”这些变量和函数。

”导出“,或者说声明本文件提供哪些变量和函数,可以通过在文件的开头使用 provide 关键字来实现。在我们的示例中,utils.rkt 文件最终应该如下:

scheme

#lang racket

(provide hello num1 num2)
(provide sum-of-cube)

(define hello "Hello, World!")
(define num1 3)
(define num2 4.2)

(define (cube x) (* x x x))

(define (sum-of-cube x y)
  (+ (cube x)
     (cube y)))

其中的 (provide hello num1 num2)(provide sum-of-cube) 分别声明了 hello, num1, num2 以及 sum-of-cubeutils.rkt 中是可以被其他文件访问的。当然,你其实可以将 (provide hello num1 num2)(provide sum-of-cube) 合并成 (provide hello num1 num2 sum-of-cube),或者将其分成四行:

scheme

(provide hello)
(provide num1)
(provide num2)
(provide sum-of-cube)

都是可以的。

最后,我们在 main.rkt 中使用 require 关键字来导入 utils.rkt 提供的变量和函数即可:

scheme

#lang racket

(require "helper.rkt")

hello ;; "Hello, World!"
num1 ;; 3
num2 ;; 4.2

(define x (sum-of-cube num1 2))
x ;; 35

注意

通常来讲,providerequire 语句应该放在文件的开头,#lang 声明之后。

另外,除了使用 (require "my-helpers.rkt") 的方式,你还可以通过 (require net/url) 的方式来导入网络上的一些其他模块,或者一些安装 Racket 时附带的模块。例如提供测试功能的 test-engine/racket-tests 模块。

测试 ​

有的时候,我们需要对我们的代码进行测试。Racket 提供了一个简单的测试框架,可以帮助我们进行测试。我们可以通过 test-engine/racket-tests 模块来使用这个测试框架。首先,我们需要导入这个模块:

scheme

#lang racket

(require test-engine/racket-tests)

然后,假如我们有一些函数需要测试,或者有一些变量值需要校验。例如:

scheme

(define (fact n)
  (cond
    [(= n 0) 1]
    [(= n 1) 1]
    [else (* n (fact (- n 1)))]))

(define x 16)
(define y 12)

我们分别需要测试斐波那契数列函数输出是否正确,以及 xy 的和是否等于 32。我们可以通过 check-expect 表达式来建立测试用例:

scheme

(check-expect (fact 5) 120)
(define (fact n)
  (cond
    [(= n 0) 1]
    [(= n 1) 1]
    [else (* n (fact (- n 1)))]))

(check-expect (+ x y) 32)
(define x 16)
(define y 12)

其中,(check-expect (fact 5) 120) 会检查 (fact 5) 表达式的值是否等于 120,而 (check-expect (+ x y) 32) 会检查 (x + y) 的值是否等于 32

注意,与其它一些语言的测试框架不同,我们可以把测试用例写在其相关的定义前面。这样我们其实可以通过文件中的空行等方式分隔不同的功能,并通过每个部分开头的测试用例来简单的描述这部分的功能。

目前,你的代码文件应该是这样的:

scheme

#lang racket

(require test-engine/racket-tests)

(check-expect (fact 5) 120)
(define (fact n)
  (cond
    [(= n 0) 1]
    [(= n 1) 1]
    [else (* n (fact (- n 1)))]))

(check-expect (+ x y) 32)
(define x 16)
(define y 12)

如果你运行这段代码,你会发现什么都没有发生。实际上,我们需要在代码的结尾加入 (test) 表达式来一次性运行所有的测试用例:

scheme

#lang racket

(require test-engine/racket-tests)

(check-expect (fact 5) 120)
(define (fact n)
  (cond
    [(= n 0) 1]
    [(= n 1) 1]
    [else (* n (fact (- n 1)))]))

(check-expect (+ x y) 32)
(define x 16)
(define y 12)

(test)

输出应该是这样的:

shell

Ran 2 tests.                                                        
1 of the 2 tests failed.                                            
Check failures:                                                     
                     ┌────┐              ┌────┐                     
        Actual value 28 differs from 32 │, the expected value.
                     └────┘              └────┘                     
in test.rkt, line 12, column 0

其中不仅会告诉一共运行了多少个测试用例,还会告诉你有多少个测试用例失败了,以及失败的原因。这就是最简单的 Racket 测试。

The testing modules also provide check-error, which checks if its single argument expression produces an error when evaluated; check-within, used to test whether an inexact computed value is within a certain tolerance of an expected value; and check-member-of, which permits several possible expected values.

这个测试框架还提供了 check-error,用于检查其参数表达式在求值时是否产生错误;check-within,用于测试计算出的非精确值是否在预期值的某个容差范围内;以及 check-member-of,允许多个可能的预期值。

Ragde [1],Cubik65536 译

总结 ​

本文简单讲述了如何通过 providerequire 表达式来在 Racket 中进行模块化开发,以及如何使用 Racket 自带的 test-engine/racket-tests 模块来进行简单的功能测试。

随着 Teach Yourself Racket 的 Basic 一章的结束,本文就是 Racket 入门系列的最后一篇文章了。虽然我们只是简单的介绍了一些 Racket 中最基础的内容,但是这些内容已经足够读者开始使用 Racket 来进行一些简单的编程了。

在即将开始的《Racket 学习笔记》系列中,我们将继续跟随 Teach Yourself Racket,了解 Racket 中的纯函数式编程(Lambda,高阶函数,本地变量),非纯函数式编程(I/O,可变性),以及 Racket 中的迭代和正则表达式。之后我们将跟随 Racket 官方的 The Racket Guide 来了解 Teach Yourself Racket 没有提到的内容,例如 Racket 的异常机制,宏,包管理等。

GL & HF!


Footer

  1. P. Ragde, "1 Basics," Teach Yourself Racket. Accessed: Jul. 23, 2024.
    Available: https://cs.uwaterloo.ca/~plragde/flaneries/TYR/Basics.html ↩︎