Racket 编程
第 1 部分 · Racket 核心

函数与过程

函数与过程

学习如何定义和使用函数,掌握高阶函数、lambda 表达式等核心概念。

定义函数

使用 define 定义命名函数:

#lang racket

;; 基本函数定义
(define (greet name)
  (string-append "Hello, " name "!"))

(greet "Racket")   ; => "Hello, Racket!"

;; 多参数函数
(define (add x y)
  (+ x y))

(add 3 4)          ; => 7

Lambda 表达式

匿名函数是函数式编程的核心:

;; lambda 定义匿名函数
(lambda (x) (* x x))

;; 直接使用
((lambda (x) (* x x)) 5)   ; => 25

;; 绑定到变量
(define square (lambda (x) (* x x)))
(square 5)                  ; => 25

;; 简化语法
(define (square x) (* x x))  ; 等价于上面的定义

高阶函数

函数可以接受函数作为参数,或返回函数:

;; 接受函数作为参数
(define (apply-twice f x)
  (f (f x)))

(apply-twice square 3)   ; => 81  (3^2=9, 9^2=81)

;; 返回函数
(define (make-adder n)
  (lambda (x) (+ x n)))

(define add5 (make-adder 5))
(add5 10)                ; => 15

常用高阶函数

;; map - 映射
(map square '(1 2 3 4 5))     ; => '(1 4 9 16 25)

;; filter - 过滤
(filter even? '(1 2 3 4 5 6)) ; => '(2 4 6)

;; foldl / foldr - 归约
(foldl + 0 '(1 2 3 4 5))      ; => 15
(foldl cons '() '(1 2 3))     ; => '(3 2 1)
(foldr cons '() '(1 2 3))     ; => '(1 2 3)

;; andmap / ormap
(andmap positive? '(1 2 3))   ; => #t
(ormap even? '(1 3 5 7 8))    ; => #t

闭包

函数可以"记住"其定义时的环境:

(define (make-counter)
  (let ([count 0])
    (lambda ()
      (set! count (+ count 1))
      count)))

(define c1 (make-counter))
(define c2 (make-counter))

(c1)   ; => 1
(c1)   ; => 2
(c2)   ; => 1 (独立的计数器)
(c1)   ; => 3

递归

递归是 Racket 中处理循环的主要方式:

;; 简单递归
(define (factorial n)
  (if (= n 0)
      1
      (* n (factorial (- n 1)))))

(factorial 5)   ; => 120

;; 尾递归(优化为循环)
(define (factorial-tail n)
  (let loop ([acc 1] [i n])
    (if (= i 0)
        acc
        (loop (* acc i) (- i 1)))))

(factorial-tail 5)  ; => 120

;; 处理列表
(define (sum lst)
  (if (null? lst)
      0
      (+ (car lst) (sum (cdr lst)))))

(sum '(1 2 3 4 5))  ; => 15

函数组合

;; compose - 函数组合
(define (compose f g)
  (lambda (x) (f (g x))))

(define add1-then-square
  (compose square add1))

(add1-then-square 4)   ; => 25 (4+1=5, 5^2=25)

;; 使用内置的 compose
((compose string-length symbol->string) 'hello)  ; => 5

小结

函数是 Racket 中的"一等公民",这意味着你可以:

  • 将函数作为参数传递
  • 从函数中返回函数
  • 将函数存储在数据结构中
  • 在运行时动态创建函数

这种灵活性使得 Racket 能够表达非常抽象的编程模式。