强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Guile/Scheme 编程教程 / 第12章:最佳实践

第 12 章:最佳实践

12.1 Guile 编程风格

12.1.1 命名规范

;; 1. 变量和函数:使用连字符分隔(kebab-case)
(define user-name "Alice")           ; ✓ 正确
(define user_name "Alice")           ; ✗ 不推荐(C 风格)
(define userName "Alice")            ; ✗ 不推荐(驼峰式)

;; 2. 谓词函数:以 ? 结尾
(define (valid? input) ...)          ; ✓ 正确
(define (is-valid input) ...)        ; 不推荐(Scheme 不用 is- 前缀)

;; 3. 修改函数:以 ! 结尾(破坏性修改)
(define (vector-set! vec i val) ...) ; ✓ 正确
(define (hash-set! ht key val) ...)  ; ✓ 正确

;; 4. 转换函数:类型 → 类型
(define (list->vector lst) ...)      ; ✓ 正确
(define (string->number str) ...)    ; ✓ 正确

;; 5. 常量:使用 * 包围
(define *max-retries* 3)            ; ✓ 正确
(define *default-timeout* 30)       ; ✓ 正确

;; 6. 内部辅助函数:使用 % 前缀
(define %internal-helper ...)       ; ✓ 正确(模块内部用)

;; 7. 记录类型:<类型名>
(define-record-type <user> ...)     ; ✓ 正确

12.1.2 代码格式化

;; 1. 括号位置:闭括号放在同一行
(define (greet name)                ; ✓ 正确
  (string-append "Hello, " name "!"))

(define (greet name)                ; ✗ 不推荐
  (string-append "Hello, " name "!"
  )
)

;; 2. 缩进:2 个空格
(define (process-items items)
  (let loop ((rest items)           ; 2 空格缩进
             (acc '()))
    (if (null? rest)
        (reverse acc)
        (loop (cdr rest)
              (cons (process (car rest)) acc)))))

;; 3. 长表达式换行
(define (long-function arg1 arg2 arg3
                       arg4 arg5)
  (let ((result (compute arg1 arg2 arg3
                          arg4 arg5)))
    result))

;; 4. cond 子句格式
(define (classify x)
  (cond
    ((number? x) "数字")         ; 每个子句 2 空格
    ((string? x) "字符串")
    ((symbol? x) "符号")
    (else "未知")))

;; 5. let 绑定对齐
(let ((name "Alice")             ; 绑定对齐
      (age 30)
      (city "Beijing"))
  (format #t "~a, ~a, ~a" name age city))

12.1.3 注释规范

;;; 文件头注释(4 个分号)
;;; my-module.scm — 用户管理模块
;;; Copyright (C) 2026 My Project

;;; 章节标题(3 个分号)
;;; ===== 用户操作 =====

;; 段落注释(2 个分号)—— 最常用
;; 这个函数处理用户输入并验证格式
(define (validate-user input)
  ...)

; 行尾注释(1 个分号)
(define timeout 30) ; 超时秒数

;;; 文档注释(3 个分号,放在定义前面)
;;; create-user — 创建新用户
;;; 参数 name: 用户名(字符串)
;;; 参数 email: 邮箱地址(字符串)
;;; 返回: <user> 记录
;;; 异常: 当 name 为空时抛出 'invalid-input
(define (create-user name email)
  (when (string-blank? name)
    (throw 'invalid-input "用户名不能为空"))
  (make-user name email))

12.2 代码组织

12.2.1 文件结构模板

;;; -*- mode: scheme; -*-
;;; filename.scm — 模块简述
;;;
;;; 详细描述...
;;; Copyright (C) 2026 Author <[email protected]>
;;; License: GPL-3.0+

;; 模块声明
(define-module (my-module)
  #:use-module (srfi srfi-1)
  #:use-module (ice-9 format)
  #:export (public-func
            public-var))

;; 常量定义
(define *version* "1.0.0")

;; 记录类型定义
(define-record-type <widget>
  (make-widget name size)
  widget?
  (name widget-name)
  (size widget-size))

;; 内部辅助函数
(define (%helper x)
  ...)

;; 公共函数
(define (public-func x)
  ...)

;; 主逻辑(如果是脚本)
(define (main args)
  ...)

12.2.2 依赖管理

;; 在模块顶部声明所有依赖
(define-module (my-module)
  ;; 标准库
  #:use-module (ice-9 format)
  #:use-module (ice-9 regex)

  ;; SRFI
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-9)

  ;; 第三方库
  #:use-module (json)

  ;; 项目内部模块
  #:use-module (my-project utils)
  #:use-module (my-project config)

  ;; 选择性导入
  #:use-module ((srfi srfi-13)
                #:select (string-join string-split))
  #:use-module ((srfi srfi-13)
                #:prefix s13/)

  ;; 导出
  #:export (my-func my-var))

12.3 性能优化

12.3.1 性能分析工具

;; 1. 使用 time 宏测量
(define-syntax time-it
  (syntax-rules ()
    ((_ label body ...)
     (let ((start (get-internal-real-time)))
       (let ((result (begin body ...)))
         (let ((end (get-internal-real-time)))
           (format #t "[~a] 耗时: ~a ms~%"
                   label
                   (/ (* (- end start) 1000)
                      internal-time-units-per-second))
           result))))))

;; 2. 使用 Guile 内置的统计
(use-modules (statprof))

(statprof-reset)
(statprof-start)
;; 要分析的代码
(let loop ((i 0))
  (when (< i 10000)
    (some-computation i)
    (loop (+ i 1))))
(statprof-stop)
(statprof-display)
;; 显示每个函数的耗时占比

;; 3. 追踪函数调用
(trace some-function)
(some-function arg1 arg2)
;; 输出每次调用和返回值
(untrace some-function)

12.3.2 性能优化策略

;; 策略 1: 使用尾递归避免栈溢出
;; 差:
(define (sum-bad lst)
  (if (null? lst) 0
      (+ (car lst) (sum-bad (cdr lst)))))

;; 好:
(define (sum-good lst)
  (let loop ((rest lst) (acc 0))
    (if (null? rest) acc
        (loop (cdr rest) (+ (car rest) acc)))))

;; 策略 2: 使用数值向量替代列表
;; 差:列表存储数值
(define data-list (iota 1000000))  ; 列表,内存不连续

;; 好:使用 SRFI-4 数值向量
(use-modules (srfi srfi-4))
(define data-vec (apply f64vector (map inexact (iota 1000000))))

;; 策略 3: 使用哈希表替代关联列表
;; 差:大列表查找 O(n)
(define big-alist (map (lambda (i) (cons i (* i i))) (iota 10000)))

;; 好:哈希表查找 O(1)
(define big-ht (make-hash-table))
(for-each (lambda (i) (hash-set! big-ht i (* i i))) (iota 10000))

;; 策略 4: 避免不必要的分配
;; 差:
(define (process items)
  (map (lambda (x) (* x 2))         ; 创建新列表
       (filter even? items)))        ; 又创建一个新列表

;; 好:单次遍历
(define (process items)
  (let loop ((rest items) (acc '()))
    (cond
      ((null? rest) (reverse acc))
      ((even? (car rest))
       (loop (cdr rest) (cons (* (car rest) 2) acc)))
      (else (loop (cdr rest) acc)))))

;; 策略 5: 缓存重复计算
(define memo-fib
  (let ((cache (make-hash-table)))
    (lambda (n)
      (or (hash-ref cache n)
          (let ((result (if (<= n 1) n
                            (+ (memo-fib (- n 1))
                               (memo-fib (- n 2))))))
            (hash-set! cache n result)
            result)))))

12.3.3 编译优化

# 编译 Guile 代码以提升性能
guild compile my-module.scm

# 指定优化级别
guild compile -O2 my-module.scm

# 批量编译整个目录
find src/ -name "*.scm" -exec guild compile {} \;

# 查看编译产物
ls src/*.go  # 编译后的字节码文件
;; 在代码中控制编译
(use-modules (system base compile))

;; 编译表达式
(compile '(+ 1 2) #:to 'value)

;; 编译文件
(compile-file "my-module.scm" #:to 'value)

;; 在模块中启用编译
(define-module (my-module)
  #:declarative? #t        ; 声明式模块,可优化
  #:autoload ...)

12.4 调试技巧

12.4.1 REPL 调试

;; 1. 使用 backtrace 查看调用栈
(catch #t
  (lambda ()
    (error-provoking-code))
  (lambda (key . args)
    (backtrace)  ; 打印完整调用栈
    (display key)))

;; 2. 使用 ,bt 在 REPL 中查看
;; scheme@(guile-user)> ,bt

;; 3. 断点式调试
(define (debug-break)
  (let ((port (current-error-port)))
    (format port "~%=== 调试断点 ===~%")
    (format port "输入 (continue) 继续执行~%")
    (break)))

;; 4. 使用 geiser 的 M-x geiser-edit-module 查看模块源码

12.4.2 日志记录

;; 简易日志框架
(define *log-level* (make-parameter 'info))
(define *log-port* (make-parameter (current-error-port)))

(define (log-message level fmt . args)
  (when (>= (log-level->number level)
            (log-level->number (*log-level*)))
    (apply format (*log-port*)
           (string-append "[" (symbol->string level) "] " fmt "~%")
           args)))

(define (log-level->number level)
  (case level
    ((debug) 0) ((info) 1) ((warn) 2) ((error) 3)
    (else 1)))

(define (log-debug fmt . args) (apply log-message 'debug fmt args))
(define (log-info fmt . args)  (apply log-message 'info fmt args))
(define (log-warn fmt . args)  (apply log-message 'warn fmt args))
(define (log-error fmt . args) (apply log-message 'error fmt args))

;; 使用
(parameterize ((*log-level* 'debug))
  (log-debug "调试信息: ~a" value)
  (log-info "处理完成")
  (log-error "出错了: ~a" error-msg))

12.4.3 单元测试

;; 使用 SRFI-64 测试框架
(use-modules (srfi srfi-64))

(test-begin "用户模块测试")

(test-group "创建用户"
  (test-assert "有效用户" (make-user "Alice" 30))
  (test-eq "用户名" "Alice" (user-name (make-user "Alice" 30)))
  (test-eq "年龄" 30 (user-age (make-user "Alice" 30))))

(test-group "验证"
  (test-assert "有效输入" (validate-input "hello"))
  (test-assert "空输入无效" (not (validate-input "")))
  (test-assert "非字符串无效" (not (validate-input 42))))

(test-end "用户模块测试")
;; 测试辅助宏
(define-syntax test-suite
  (syntax-rules ()
    ((_ name test ...)
     (begin
       (test-begin name)
       test ...
       (test-end name)))))

(define-syntax test-macro-expansion
  (syntax-rules ()
    ((_ macro expected)
     (test-equal (quote macro)
                 (quote expected)
                 (syntax->datum (expand (quote macro)))))))

;; 使用
(test-suite "数学函数测试"
  (test-eq "平方" 25 (square 5))
  (test-eq "立方" 27 (cube 3))
  (test-approximate "平方根" 1.414 (sqrt 2) 0.001))

12.5 Guix 系统配置

12.5.1 Guix 系统配置基础

;; /etc/config.scm — 完整的 Guix 系统配置
(use-modules (gnu)
             (gnu system)
             (gnu system nss)
             (gnu packages)
             (gnu packages admin)
             (gnu packages guile)
             (gnu packages version-control)
             (gnu packages networking)
             (gnu services)
             (gnu services ssh)
             (gnu services networking)
             (gnu services web))

(operating-system
  (host-name "guile-server")
  (timezone "Asia/Shanghai")
  (locale "zh_CN.utf8")

  ;; 键盘布局
  (keyboard-layout (keyboard-layout "cn"))

  ;; 引导加载器
  (bootloader (bootloader-configuration
                (bootloader grub-bootloader)
                (targets '("/dev/sda"))
                (keyboard-layout keyboard-layout)))

  ;; 文件系统
  (file-systems (append
                  (list (file-system
                          (device (file-system-label "root"))
                          (mount-point "/")
                          (type "ext4")))
                  %base-file-systems))

  ;; 用户账户
  (users (cons (user-account
                 (name "developer")
                 (comment "Guile 开发者")
                 (group "users")
                 (home-directory "/home/developer")
                 (supplementary-groups '("wheel" "netdev" "audio" "video")))
               %base-user-accounts))

  ;; 系统级包
  (packages (append (list guile-3.0
                          git
                          nss-certs
                          inetd)
                    %base-packages))

  ;; 服务
  (services (append
              (list (service openssh-service-type
                            (openssh-configuration
                              (port-number 22)
                              (permit-root-login #f)))
                    (service dhcp-client-service-type))
              %base-services)))

12.5.2 自定义 Guix 服务

;; 定义自定义服务
(use-modules (gnu services)
             (gnu services shepherd)
             (gnu system shadow)
             (guix gexp)
             (guix records))

;; 服务配置记录
(define-record-type <my-app-config>
  (make-my-app-config port log-file data-dir)
  my-app-config?
  (port my-app-config-port)
  (log-file my-app-config-log-file)
  (data-dir my-app-config-data-dir))

;; 默认配置
(define %default-my-app-config
  (make-my-app-config 8080 "/var/log/my-app.log" "/var/lib/my-app"))

;; Shepherd 服务定义
(define (my-app-shepherd-service config)
  (list (shepherd-service
          (provision '(my-app))
          (requirement '(networking))
          (documentation "My Guile Web App")
          (start #~(make-forkexec-constructor
                     (list #$(file-append guile-3.0 "/bin/guile")
                           "-e" "main"
                           #$(local-file "../app/main.scm"))
                     #:environment-variables
                     (list (string-append "PORT="
                                          (number->string
                                            #$(my-app-config-port config))))))
          (stop #~(make-kill-destructor)))))

;; 服务类型
(define my-app-service-type
  (service-type
    (name 'my-app)
    (extensions
      (list (service-extension shepherd-root-service-type
                               my-app-shepherd-service)))
    (default-value %default-my-app-config)
    (description "运行 Guile Web 应用")))

12.5.3 Guix 包定义

;; 定义自定义 Guix 包
(use-modules (guix packages)
             (guix build-system gnu)
             (guix licenses)
             (guix gexp)
             (guix git-download)
             (gnu packages guile))

(define-public my-guile-lib
  (package
    (name "my-guile-lib")
    (version "1.0.0")
    (source (origin
              (method git-fetch)
              (uri (git-reference
                     (url "https://github.com/user/my-guile-lib")
                     (commit (string-append "v" version))))
              (sha256
                (base32 "0abc..."))))
    (build-system gnu-build-system)
    (arguments
      '(#:phases
        (modify-phases %standard-phases
          (add-after 'install 'wrap-program
            (lambda* (#:key outputs #:allow-other-keys)
              (let ((out (assoc-ref outputs "out")))
                (wrap-program (string-append out "/bin/my-app")
                  `("GUILE_LOAD_PATH" =
                    (,(string-append out "/share/guile/site/3.0"))))))))))
    (native-inputs
      (list guile-3.0))
    (home-page "https://github.com/user/my-guile-lib")
    (description "我的 Guile 工具库")
    (license gpl3+)))

12.6 项目模板

12.6.1 推荐的项目结构

my-guile-project/
├── .gitignore
├── LICENSE
├── README.md
├── Makefile
├── guix.scm                    # Guix 环境定义
├── manifest.scm                # Guix 依赖清单
├── configure.ac                # 可选:autotools 配置
├── my-project/
│   ├── core.scm
│   ├── utils.scm
│   ├── web/
│   │   ├── handler.scm
│   │   └── middleware.scm
│   └── db/
│       ├── connection.scm
│       └── query.scm
├── tests/
│   ├── test-core.scm
│   ├── test-utils.scm
│   └── test-web.scm
├── scripts/
│   └── run.sh
└── doc/
    └── api.md

12.6.2 项目 Makefile 模板

# Makefile for Guile Project

GUILE     = guile
GUILEC    = guild compile
GUILD     = guild
TEST_RUNNER = $(GUILE) -L . -e main

# 源码目录
SRC_DIR   = my-project
TEST_DIR  = tests

# 源码文件
SOURCES   = $(shell find $(SRC_DIR) -name "*.scm")
COMPILED  = $(SOURCES:.scm=.go)

# 默认目标
.PHONY: all build test clean install

all: build

# 编译
build: $(COMPILED)

%.go: %.scm
	$(GUILEC) -L $(SRC_DIR) $<

# 测试
test:
	$(GUILE) -L $(SRC_DIR) $(TEST_DIR)/test-core.scm
	$(GUILE) -L $(SRC_DIR) $(TEST_DIR)/test-utils.scm

# 清理
clean:
	find . -name "*.go" -delete
	rm -rf public/

# 安装
install:
	$(GUILD) compile $(SRC_DIR)
	mkdir -p $(DESTDIR)/share/guile/site/3.0
	cp -r $(SRC_DIR) $(DESTDIR)/share/guile/site/3.0/

# 开发环境
dev:
	guix shell -m manifest.scm

# 文档
doc:
	guix shell texinfo -- makeinfo doc/api.texi

12.6.3 .gitignore 模板

# 编译产物
*.go
*.bak

# Guix
.guix-profile

# 编辑器
*~
\#*\#
.dir-locals.el
.vscode/

# 系统
.DS_Store
Thumbs.db

# 构建
/public/
/build/
/dist/

12.7 学习路径建议

12.7.1 初学者路径

第1周: 第1-3章(概述、安装、基本语法)
       → 熟悉 REPL,理解 S-表达式和求值规则

第2周: 第4-5章(列表、函数)
       → 掌握列表操作、lambda 和闭包

第3周: 第6章(控制流)
       → 条件、绑定、递归、尾调用

第4周: 第8章(数据结构)
       → 记录、向量、哈希表

实践: 用 Guile 写一个小工具脚本

12.7.2 进阶路径

第5-6周: 第7章(宏系统)
         → syntax-rules、define-macro

第7-8周: 第9-10章(模块系统、I/O)
         → 项目组织、文件操作、网络

实践: 开发一个有多个模块的库

12.7.3 高级路径

第9-10周: 第11-12章(C扩展、最佳实践)
          → FFI、性能优化、Guix 配置

实践:
1. 封装一个 C 库
2. 编写 Guix 包定义
3. 为开源项目贡献 Guile 代码

12.8 推荐资源

资源类型说明
Guile 官方手册文档最权威的参考
SICP书籍经典计算机科学教材
The Scheme Programming Language书籍R. Kent Dybvig 著
Guile Cookbook文档实用代码片段
SRFI 索引标准Scheme 扩展标准库
Guix Manual文档Guix 系统管理
#guile @ Libera.Chat社区IRC 实时交流
guile-user 邮件列表社区邮件列表讨论

12.9 本章小结

主题要点
命名规范kebab-case、?/! 后缀、常量
代码格式括号同行、2 空格缩进
性能优化尾递归、数值向量、哈希表、JIT
调试REPL 命令、statprof、SRFI-64 测试
Guix 配置系统定义、服务定义、包定义
项目模板标准目录结构和构建脚本

扩展阅读


上一章:第 11 章:C 扩展与 FFI 返回:教程目录