定义与实战)
文档教程知识库【免费下载链接】til:memo: Today I Learned项目地址https://gitcode.com/gh_mirrors/ti/til点击查看免费下载Elixir 的模式匹配能力不仅属于命名函数def同样适用于匿名函数fn你可以在一个fn中按不同输入形状或值定义多个子句clause从而把「分支逻辑」内联到函数本身。本文以仓库文档 defining-multiple-clauses-in-an-anonymous-function.md 为核心结合 pattern-matching-in-anonymous-functions.md 等同主题笔记讲解多子句匿名函数的定义语法、执行语义、卫语句guard配合、实际应用场景与边界限制读完即可在 IEx 或真实项目中写出更简洁、更具表达力的分支处理代码。一、为什么要给匿名函数定义多个子句匿名函数最常见的写法是「对输入做单一处理」——无论输入长什么样都走同一段逻辑。但这并非强制Elixir 程序中无处不在的模式匹配同样可以用于匿名函数为不同形状或不同值的输入触发不同行为。典型的例子如下来自 defining-multiple-clauses-in-an-anonymous-function.mdiex my_function fn {:ok, x} - Everything is ok: #{x} {:error, x} - There was an error: #{x} end #Function6.52032458/1 in :erl_eval.expr/5这里my_function是一个单参数arity 为 1的匿名函数但内部有两个子句第一个子句匹配{:ok, x}第二个子句匹配{:error, x}。调用时按子句声明的顺序从上到下尝试匹配第一个成功匹配的子句生效iex my_function.({:ok, 123}) Everything is ok: 123 iex my_function.({:error, be warned}) There was an error: be warned同一个函数变量输入{:ok, ...}与{:error, ...}得到完全不同的处理路径这正是多子句匿名函数的价值把「判断 处理」压缩成一个可复用的函数值。二、语法与执行语义子句顺序与首个匹配生效多子句匿名函数的标准语法是fn pattern1 - expression1 pattern2 - expression2 ... end执行时遵循两条关键规则顺序匹配子句按书写顺序自上而下逐一尝试匹配输入首个匹配生效一旦某个子句的模式与参数匹配成功且通过守卫条件该子句体立即执行后续子句不再参与完备性由你负责如果没有任何子句匹配运行时会抛出FunctionClauseError详见下文第五节。因此子句的排列顺序就是分支的优先级。例如下面的写法中{:ok, _}与具体的{:ok, 0}同时存在时先写谁谁就先被命中iex pick fn {:ok, 0} - zero {:ok, x} - positive: #{x} end iex pick.({:ok, 0}) zero这与 pattern-matching-in-anonymous-functions.md 中描述的思路一脉相承——模式匹配「无处不在」包括匿名函数声明处 handle_result fn {:ok, result} - IO.puts The result is #{result} :error - IO.puts Error: couldnt find anything end Map.fetch(%{a: 1}, :a) | handle_result.() The result is 1 :ok Map.fetch(%{a: 1}, :b) | handle_result.() Error: couldnt find anything :ok注意这里子句模式还可以是不同形状第一个子句匹配二元元组{:ok, result}第二个子句匹配裸原子:error——同一个函数可以优雅地消化多种输入形态。三、用when卫语句进一步收窄匹配模式只能表达结构约束而数值范围、比较等条件需要用卫语句guard。匿名函数的多子句与命名函数一样支持wheniex classify fn n when n 0 - negative n when n 0 - zero n - positive end iex classify.(-5) negative iex classify.(0) zero iex classify.(42) positive最后一个没有when的子句作为兜底catch-all避免未匹配时抛出异常。这种「模式 卫语句」的组合能力与仓库中 using-when-clauses-in-a-with-construct.md 所展示的思想一致——Elixir 的模式匹配在全语言层面都支持用when收窄匹配范围例如只命中 2xx 响应码with %{status_code: code, body: body} when code 200 code 300 - HTTPoison.get!(url), {:ok, decoded_body} - Poison.decode(body) do {:ok, decoded_body} end在匿名函数中同样可以结合when与结构匹配例如只处理特定状态码的响应iex handler fn %{status_code: code, body: body} when code in 200..299 - OK: #{body} %{status_code: code} when code in 400..499 - Client error: #{code} _ - Unknown end四、实战场景与Map.fetch/2、管道等惯用法组合多子句匿名函数最常见的落地场景是把「处理不同结果形态」的逻辑做成一个函数值再配合 Elixir 惯用法管道、Map、Enum等使用。场景一处理查找结果。上面的handle_result示例配合Map.fetch/2与管道运算符|可以写出非常流畅的链式调用 Map.fetch(%{a: 1}, :a) | handle_result.() The result is 1 :ok Map.fetch(%{a: 1}, :b) | handle_result.() Error: couldnt find anything :okMap.fetch/2返回{:ok, value}或:error正好与匿名函数的两个子句一一对应管道把上一次调用的结果作为参数传给多子句匿名函数。类似的思路也可参考 pipe-into-a-case-statement.md——把分支逻辑放到管线末端让代码的阅读顺序与数据流方向一致。场景二在Enum高阶函数中内联分支。多子句匿名函数作为回调传给Enum.map/2、Enum.reduce/3等函数时可以让回调本身具备「按值分流」的能力iex entries [%{kind: :ok, data: 1}, %{kind: :error, msg: boom}, %{kind: :ok, data: 2}] iex Enum.map(entries, fn %{kind: :ok, data: d} - {:processed, d * 10} %{kind: :error, msg: m} - {:failed, m} end) [{:processed, 10}, {:failed, boom}, {:processed, 20}]场景三状态/命令模式。需要根据输入的不同形状分发处理时用一个多子句匿名函数即可替代小型caseiex execute fn {:add, a, b} - a b {:mul, a, b} - a * b {:pow, a, b} - :math.pow(a, b) end iex execute.({:add, 2, 3}) 5五、没有子句匹配会发生什么FunctionClauseError多子句匿名函数没有默认兜底。如果调用时输入与所有子句都不匹配运行时会抛出FunctionClauseErroriex my_function fn {:ok, x} - Everything is ok: #{x} {:error, x} - There was an error: #{x} end iex my_function.(:other) ** (FunctionClauseError) no function clause matching in :erl_eval.apply/3这也是把「分支逻辑」显式化带来的代价调用方必须保证输入落入已定义的子句集合。实践上通常有两种选择严格模式故意不提供兜底子句让不符合预期的输入快速失败fail fast尽早暴露调用方 bug宽松模式在最后一个子句使用_通配符或带when的兜底子句对未预期的输入给出默认处理避免线上抛异常。iex safe fn {:ok, x} - Everything is ok: #{x} _ - Unknown input end iex safe.(:other) Unknown input六、多子句匿名函数的边界与注意事项6.1 捕获运算符只能定义单子句使用捕获语法定义匿名函数时只能写一个表达式、一个子句无法表达多子句模式# 写法单子句无法按输入形状分流 iex square (1 * 1) # 需要多子句时只能用 fn 显式定义 iex handle fn {:ok, x} - x {:error, _} - nil end6.2 子句内变量作用域相互独立每个子句的模式变量只在该子句体内可见不能跨子句引用。例如下面的x在两个子句中分别绑定互不干扰iex f fn {:ok, x} - ok #{x} {:error, x} - err #{x} end如果某个变量需要在所有子句中共享例如来自外层闭包捕获应在外层先绑定再在子句体内引用iex prefix result: iex f fn {:ok, x} - prefix ok #{x} {:error, x} - prefix err #{x} end6.3 与命名函数多子句、case的取舍多子句匿名函数、命名函数多子句def与case表达能力等价取舍要点如下方式适用场景fn多子句需要把「带分支的处理逻辑」作为一个函数值传递回调、管道、闭包时def多子句在模块中定义可复用、可测试、支持递归与头head约定的公共函数时case分支逻辑是「一次性判断」无需抽象成函数值且希望多个分支共享同一段后续逻辑时仓库中的 pipe-into-a-case-statement.md 展示了case的管道用法而本文展示的是把同样逻辑内联进匿名函数——两者互为替代方案选哪种取决于你是否需要把这个「分支处理器」当作一等公民传来传去。七、小结多子句匿名函数是 Elixir「模式匹配无处不在」哲学在函数值层面的体现用fn的多个子句按输入形状或值分流处理子句按顺序匹配、首个命中生效可结合when卫语句收窄匹配范围也可用_兜底无子句匹配时抛出FunctionClauseError可用于快速失败或借兜底子句降级与Map.fetch/2、管道、Enum高阶函数配合能把「判断 处理」封装成可复用的函数值局限性在于捕获语法无法表达多子句且子句间变量作用域相互独立。想深入本仓库更多相关内容可继续阅读 pattern-matching-in-anonymous-functions.md匿名函数中的模式匹配基础、using-when-clauses-in-a-with-construct.md卫语句在with中的应用以及 pipe-into-a-case-statement.md管道与分支语句的组合。赞分享文档教程知识库【免费下载链接】til:memo: Today I Learned项目地址https://gitcode.com/gh_mirrors/ti/til点击查看免费下载相关推荐掌握Elixir函数定义从匿名函数到命名函数的完整指南掌握Elixir函数定义从匿名函数到命名函数的完整指南 Elixir作为一种动态函数式编程语言其函数系统是构建可扩展应用程序的核心。本文将详细介绍Elixi编程语言编译器标准库语言运行时并发编程Feathers 匿名认证实战自定义 AnonymousStrategy 与 allow-anonymous 钩子完整指南Feathers 匿名认证实战自定义 AnonymousStrategy 与 allow anonymous 钩子完整指南 匿名认证Anonymous Au后端Web框架金庸群侠传3D重制版jynewxLua API 全解析C 与 Lua 互操作核心接口实战指南金庸群侠传3D重制版jynewxLua API 全解析C 与 Lua 互操作核心接口实战指南 本篇技术指南以开源仓库 jynew金庸群侠传3D重制版J文档教程知识库上一篇Ultimate Plumber单元测试实践up_test.go中的测试策略下一篇React Suite组件库构建流程详解从源码到npm包发布创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考