文章目录

Clojure 函数定义:defn 与参数

发布于 2026-04-06 14:45:19 · 浏览 10 次 · 评论 0 条

Clojure 函数定义:defn 与参数

Clojure 作为一门函数式编程语言,函数是其核心构建块。定义函数最常用的工具是 defn 宏。掌握 defn 的语法结构与参数处理方式,是编写清晰、简洁 Clojure 代码的关键。


1. 基础函数定义

使用 defn创建 一个标准的命名函数。它主要由函数名、可选的文档字符串、参数向量以及函数体组成。

  1. 打开 Clojure REPL(交互式解释器)。
  2. 输入 以下代码 定义 一个最简单的函数:
(defn say-hello []
  (println "Hello, Clojure!"))
  1. 输入 (say-hello) 调用 该函数。
  2. 观察 控制台输出 Hello, Clojure!

解析 上述代码结构:

  • defn:用于定义函数的宏。
  • say-hello:函数名称,符号类型。
  • []:参数向量,此处为空,表示无参数。
  • (println ...):函数体,执行具体逻辑。

添加 参数以增加函数的灵活性。

  1. 修改 函数定义,加入 参数向量 [name]
(defn greet [name]
  (str "Hello, " name "!"))
  1. 输入 (greet "Developer") 执行 函数。
  2. 查看 返回结果 "Hello, Developer!"

2. 多元数函数

Clojure 支持函数重载,允许同一个函数名根据参数个数的不同执行不同的逻辑。这被称为“多元数定义”。

  1. 编写 包含两个不同参数列表的函数。使用 列表包裹多组定义:
(defn messenger
  ([name]
   (str "Message for: " name))
  ([name content]
   (str "To " name ": " content)))
  1. 输入 (messenger "Alice") 测试 单参数版本。
  2. 输入 (messenger "Bob" "Meeting at 10am") 测试 双参数版本。

理解 参数匹配流程:

graph TD A["调用 messenger"] --> B{"判断参数数量"} B -- "数量 = 1" --> C["执行 [name] 逻辑体"] B -- "数量 = 2" --> D["执行 [name content] 逻辑体"] B -- "其他数量" --> E["抛出 ArityException 错误"]

3. 可变参数处理

当函数需要接受任意数量的参数时,使用 & 符号定义可变参数。& 后面的符号会绑定剩余的所有参数,形成一个列表。

  1. 定义 一个接受任意数量数字的求和函数:
(defn sum-all [x & rest]
  (apply + x rest))
  1. 输入 (sum-all 1 2 3 4 5) 调用 函数。
  2. 分析 参数绑定情况:
    • x 绑定第一个参数 1
    • rest 绑定剩余参数列表 (2 3 4 5)
    • apply 函数将列表展开传递给 + 函数进行计算。

注意:可变参数符号 & 只能出现在参数向量的最后位置。


4. 参数解构

Clojure 允许直接在参数列表中解构复杂数据结构,无需在函数体内手动提取数据。这使得代码意图更加明确。

使用 关联解构处理 Map 数据。

  1. 假设 传入一个包含用户信息的 Map。
  2. 编写 函数直接提取特定键值:
(defn print-user [{:keys [name age]}]
  (println "Name:" name)
  (println "Age:" age))
  1. 输入 以下代码 调用 函数:
(print-user {:name "Alice" :age 30 :id 101})
  1. 验证 输出结果:
    • :keys [name age] 自动从 Map 中提取 :name:age 对应的值。
    • Map 中未在解构列表中声明的键(如 :id)会被忽略。

对比 常见解构类型与应用场景:

解构类型 语法示例 适用场景 示例说明
顺序解构 [a b c] 处理列表、向量等序列数据。 将向量 [1 2 3] 拆分为变量 a, b, c
关联解构 {:keys [k1 k2]} 处理 Map 数据。 从 Map 中直接提取 :k1, :k2 对应的值。
混合解构 [{:keys [title]} author] 处理嵌套复杂数据结构。 第一个元素是 Map(解构 title),第二个元素绑定给 author。

5. 前置条件与后置条件

Clojure 函数支持在定义时直接加入断言,用于检查输入参数和输出结果的有效性。

  1. 使用 :pre:post 向量 定义 条件:
(defn calculate-area [radius]
  {:pre [(pos? radius)]
   :post [(pos? %)]}
  (* Math/PI radius radius))
  1. 输入 (calculate-area 5) 验证 正常流程。
  2. 输入 (calculate-area -1) 触发 前置条件失败。
    • :pre 检查 radius 是否为正数。
    • :post 检查返回值(% 代表返回值)是否为正数。
    • 若条件不满足,程序抛出 AssertionError

6. 私有函数

区分 模块内部使用的函数与对外公开的接口。

  1. 使用 defn- 替代 defn 定义 私有函数:
(defn- helper-calc [x]
  (* x x))

(defn public-api [x]
  (+ (helper-calc x) x))
  1. 当前命名空间内 调用 (helper-calc 3)确认 执行成功。
  2. 尝试 在其他命名空间引用 helper-calc
    • 编译器会报错,提示无法解析该符号。
    • 这有助于隐藏实现细节,仅暴露必要的 API。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文