单元1 · Elixir 环境与基础语法
Erlang VM、iex、IO.puts、变量、不可变数据
- 输出:IO.puts/inspect;基于 BEAM 虚拟机。
- 变量:可重新绑定;数据不可变。
- 原子::atom 常量;true/false 是原子。
实训1.1 Hello World 与输出
编写第一个 Elixir 程序,输出问候语。
IO.puts 输出;Elixir 基于 Erlang VM(BEAM)。
IO.puts("Hello, Elixir!")
IO.puts("Elixir 是函数式编程语言")
IO.inspect([1, 2, 3], label: "列表")
实训1.2 变量与不可变性
声明变量并输出,说明重绑定与不可变性。
变量可重新绑定;数据本身不可变;= 是模式匹配。
name = "Elixir"
version = 1.17
IO.puts("语言:#{name}")
IO.puts("版本:#{version}")
version = 2.0
IO.puts("重新绑定版本:#{version}")
实训1.3 原子与布尔
声明原子(atom)与布尔值并输出。
原子 :name 是不可变常量;true/false 本身是原子。
status = :ok
error = :error
is_ready = true
IO.puts("状态:#{status}")
IO.puts("错误:#{error}")
IO.puts("就绪:#{is_ready}")
IO.puts("原子相等:#{:ok == status}")
实训1.4 输入与转换
用 IO.gets 读取输入并转换为整数,输出计算结果。
IO.gets 读取字符串;String.trim 去空白;String.to_integer 转换。
IO.write("请输入一个整数:")
input = IO.gets("") |> String.trim()
n = String.to_integer(input)
IO.puts("#{n} 的平方是 #{n * n}")
单元2 · 数据类型与运算符
数值、字符串、列表、元组、算术/比较/逻辑
- 运算:div/rem 整除取余;** 幂。
- 结构:列表 [1,2,3];元组 {:ok, v}。
- 匹配:= 是模式匹配,可解构。
实训2.1 基础运算
给定整数 17 和 5,输出和、差、积、整除商、余数、幂。
div/rem 整除与取余;** 幂运算。
a = 17
b = 5
IO.puts("和:#{a + b}")
IO.puts("差:#{a - b}")
IO.puts("积:#{a * b}")
IO.puts("整除商:#{div(a, b)}")
IO.puts("余数:#{rem(a, b)}")
IO.puts("2 的 10 次方:#{Integer.pow(2, 10)}")
实训2.2 浮点运算
计算半径为 7.5 的圆的周长与面积。
:math.pi 圆周率;Float.round 四舍五入。
r = 7.5
circumference = 2 * :math.pi() * r
area = :math.pi() * r * r
IO.puts("周长:#{Float.round(circumference, 4)}")
IO.puts("面积:#{Float.round(area, 4)}")
实训2.3 比较与逻辑运算
a=8、b=12,输出 a>b、a!=b、a<10 && b>10、!(a==8)。
== 值相等;and/or/not 严格逻辑;&&/|| 宽松。
a = 8
b = 12
IO.puts("a > b:#{a > b}")
IO.puts("a != b:#{a != b}")
IO.puts("a<10 && b>10:#{a < 10 && b > 10}")
IO.puts("!(a == 8):#{not (a == 8)}")
实训2.4 列表与元组
创建列表与元组,演示模式匹配解构。
列表 [1,2,3];元组 {:ok, value};= 模式匹配。
list = [1, 2, 3, 4]
IO.puts("列表:#{inspect(list)}")
IO.puts("头部:#{hd(list)}")
IO.puts("尾部:#{inspect(tl(list))}")
IO.puts("长度:#{length(list)}")
tuple = {:ok, "success"}
{:ok, message} = tuple
IO.puts("元组消息:#{message}")
IO.puts("元组大小:#{tuple_size(tuple)}")
单元3 · 模式匹配与流程控制
= 匹配、case、cond、if、守卫
- case:值匹配 + when 守卫。
- cond:多条件分支;true 兜底。
- if/unless:宏;unless 为反面。
实训3.1 case 判断成绩等级
成绩 score=85,用 case 输出等级。
case 按值匹配;_ 通配兜底。
score = 85
case score do
s when s >= 90 -> IO.puts("优秀")
s when s >= 80 -> IO.puts("良好")
s when s >= 60 -> IO.puts("及格")
_ -> IO.puts("不及格")
end
实训3.2 cond 多条件
用 cond 实现多条件分支判断成绩。
cond 依次求值第一个为真的分支;true -> 兜底。
score = 85
cond do
score >= 90 -> IO.puts("优秀")
score >= 80 -> IO.puts("良好")
score >= 60 -> IO.puts("及格")
true -> IO.puts("不及格")
end
实训3.3 if 与 unless
用 if/unless 判断奇偶与成年。
if/unless 均为宏;unless 是 if 的反面。
n = 15
if rem(n, 2) == 0 do
IO.puts("#{n} 是偶数")
else
IO.puts("#{n} 是奇数")
end
age = 17
unless age >= 18 do
IO.puts("未成年")
end
实训3.4 守卫子句
用 when 守卫让函数/分支按条件分发。
when 守卫在匹配后附加条件。
defmodule Demo do
def classify(n) when n > 100, do: "大整数 #{n}"
def classify(n) when n > 0, do: "正数 #{n}"
def classify(n) when n < 0, do: "负数 #{n}"
def classify(_), do: "零"
end
IO.puts(Demo.classify(200))
IO.puts(Demo.classify(42))
IO.puts(Demo.classify(-5))
IO.puts(Demo.classify(0))
单元4 · 函数与递归
匿名函数、命名函数、管道、递归、尾递归
- 匿名函数:fn -> end;&1 捕获。
- 命名函数:defmodule + def;\\ 默认参数。
- 递归:模式匹配子句;尾递归优化。
实训4.1 匿名函数
定义匿名函数并调用,演示捕获运算符 &。
fn -> end 定义匿名函数;&(&1 + 1) 捕获简写。
add = fn a, b -> a + b end
IO.puts("add.(10, 20) = #{add.(10, 20)}")
square = &(&1 * &1)
IO.puts("square.(7) = #{square.(7)}")
实训4.2 命名函数与默认参数
定义带默认参数的命名函数并调用。
defmodule 组织模块;def 定义函数;\\ 默认参数。
defmodule Math do
def add(a, b), do: a + b
def greet(name, greeting \\ "你好") do
"#{greeting},#{name}!"
end
end
IO.puts("add(10, 20) = #{Math.add(10, 20)}")
IO.puts(Math.greet("小明"))
IO.puts(Math.greet("小红", "早上好"))
实训4.3 递归与模式匹配
用递归求和 1 到 n,理解递归结构。
递归替代循环;函数子句模式匹配;0 为基准情形。
defmodule Sum do
def sum(0), do: 0
def sum(n) when n > 0, do: n + sum(n - 1)
end
IO.puts("1 到 100 的和:#{Sum.sum(100)}")
IO.puts("1 到 10 的和:#{Sum.sum(10)}")
实训4.4 尾递归与累加器
用累加器实现尾递归求阶乘与列表求和。
尾递归优化(TCO);累加器传递中间结果。
defmodule Fact do
def factorial(n), do: factorial(n, 1)
defp factorial(0, acc), do: acc
defp factorial(n, acc), do: factorial(n - 1, acc * n)
def sum_list(list), do: sum_list(list, 0)
defp sum_list([], acc), do: acc
defp sum_list([head | tail], acc), do: sum_list(tail, acc + head)
end
IO.puts("5! = #{Fact.factorial(5)}")
IO.puts("列表求和:#{Fact.sum_list([1, 2, 3, 4, 5])}")
单元5 · 集合与枚举
列表操作、Enum 模块、Map、Keyword 列表
- 列表:++ 拼接;hd/tl。
- Enum:map/filter/reduce/group_by。
- Map:%{} 定义;Map.put/get。
实训5.1 列表操作
创建列表,添加元素、遍历、求最值、排序。
++ 拼接;hd/tl 头尾;Enum 模块函数。
nums = [12, 45, 7, 89, 33]
nums2 = nums ++ [60]
IO.puts("原列表:#{inspect(nums)}")
IO.puts("添加后:#{inspect(nums2)}")
IO.puts("大小:#{length(nums2)}")
IO.puts("最大值:#{Enum.max(nums2)}")
IO.puts("排序:#{inspect(Enum.sort(nums2))}")
实训5.2 Enum 常用函数
用 Enum.map、filter、reduce、each 处理数据。
Enum 模块函数式操作;管道 |> 串联。
nums = [1, 2, 3, 4, 5, 6]
doubled = Enum.map(nums, fn n -> n * 2 end)
IO.puts("翻倍:#{inspect(doubled)}")
evens = Enum.filter(nums, &(rem(&1, 2) == 0))
IO.puts("偶数:#{inspect(evens)}")
sum = Enum.reduce(nums, 0, fn n, acc -> acc + n end)
IO.puts("总和:#{sum}")
nums
|> Enum.map(&(&1 * 10))
|> Enum.filter(&(&1 > 30))
|> Enum.each(&IO.puts("管道值:#{&1}"))
实训5.3 Map 映射
创建 Map,增删改查并遍历。
%{} 定义 Map;Map.put/get/delete;点语法访问。
ages = %{zhang: 20, li: 22}
ages = Map.put(ages, :wang, 24)
ages = Map.put(ages, :li, 23)
ages = Map.delete(ages, :wang)
Enum.each(ages, fn {name, age} ->
IO.puts("#{name}:#{age} 岁")
end)
IO.puts("张三年龄:#{Map.get(ages, :zhang)}")
IO.puts("键列表:#{inspect(Map.keys(ages))}")
实训5.4 分组与归约
用 Enum.group_by、max_by、frequencies 统计处理。
group_by 分组;max_by 找最大;frequencies 频次。
scores = [55, 90, 72, 88, 45, 98]
groups = Enum.group_by(scores, fn s -> if s >= 60, do: "及格", else: "不及格" end)
Enum.each(groups, fn {k, v} -> IO.puts("#{k}:#{length(v)} 人") end)
IO.puts("最高分:#{Enum.max(scores)}")
text = "elixir is functional elixir is concurrent elixir is fun"
words = String.split(text)
freq = Enum.frequencies(words)
IO.puts("词频:#{inspect(freq)}")
top = Enum.max_by(freq, fn {_, c} -> c end)
IO.puts("出现最多:#{elem(top, 0)},#{elem(top, 1)} 次")
单元6 · 管道与高阶
管道操作符、匿名函数、捕获、函数作为值
- 管道:|> 串联函数调用。
- 捕获:&1/&2 占位参数。
- 高阶:函数一等公民;fun.(args)。
实训6.1 管道操作符
用 |> 串联多个函数调用。
管道把左侧结果作为右侧第一个参数。
result = [1, 2, 3, 4, 5, 6]
|> Enum.filter(&(rem(&1, 2) == 0))
|> Enum.map(&(&1 * &1))
|> Enum.sum()
IO.puts("偶数平方和:#{result}")
" hello, elixir "
|> String.trim()
|> String.upcase()
|> IO.puts()
实训6.2 捕获操作符 &
用 & 简写匿名函数,捕获占位符。
&1/&2 占位参数;&(&1 * 2) 等价 fn x -> x * 2 end。
double = &(&1 * 2)
IO.puts("double.(5) = #{double.(5)}")
add = &(&1 + &2)
IO.puts("add.(3, 4) = #{add.(3, 4)}")
nums = [1, 2, 3, 4]
IO.puts("map 翻倍:#{inspect(Enum.map(nums, &(&1 * 2)))}")
实训6.3 函数作为参数
编写接收函数的更高阶函数。
函数是一等公民;fun.(args) 调用。
defmodule Higher do
def apply_twice(x, fun), do: fun.(fun.(x))
def apply_to_list(list, fun), do: Enum.map(list, fun)
end
inc = &(&1 + 1)
IO.puts("apply_twice(3, +1) = #{Higher.apply_twice(3, inc)}")
IO.puts("批量应用:#{inspect(Higher.apply_to_list([1, 2, 3], &(&1 * 10)))}")
实训6.4 列表归约与模式
用 reduce 实现 fold,用模式匹配处理列表。
reduce 归约;[head | tail] 解构;匿名函数多子句。
sum = [1, 2, 3, 4, 5]
|> Enum.reduce(0, fn n, acc -> acc + n end)
IO.puts("reduce 求和:#{sum}")
[first | rest] = [10, 20, 30, 40]
IO.puts("头部:#{first}")
IO.puts("尾部:#{inspect(rest)}")
process = fn
[] -> "空列表"
[head | _] when head > 100 -> "大头部 #{head}"
[head | _] -> "普通头部 #{head}"
end
IO.puts(process.([]))
IO.puts(process.([200, 1]))
IO.puts(process.([5, 6]))
单元7 · 模块与协议
模块组织、defstruct、协议 Protocol、行为 Behaviour
- 模块:defmodule + defp 私有。
- struct:defstruct 定义字段。
- 协议/行为:defprotocol 多态;@callback 接口。
实训7.1 模块与函数组织
创建带私有函数与文档注释的模块。
defmodule 模块;defp 私有函数;@doc 文档。
defmodule Calculator do
@doc "计算两数之和"
def add(a, b), do: a + b
def add_list(list), do: Enum.sum(list)
defp format(n), do: "结果:#{n}"
def show(a, b) do
add(a, b) |> format()
end
end
IO.puts(Calculator.add(3, 4))
IO.puts(Calculator.add_list([1, 2, 3]))
IO.puts(Calculator.show(5, 6))
实训7.2 struct 结构体
用 defstruct 定义带字段的结构体并操作。
defstruct 定义字段;%User{} 创建;点语法访问。
defmodule User do
defstruct name: "", age: 0
end
u = %User{name: "小明", age: 20}
IO.puts("姓名:#{u.name},年龄:#{u.age}")
u2 = %{u | age: 21}
IO.puts("更新后:#{u2.age}")
IO.puts("默认结构:#{inspect(%User{})}")
实训7.3 协议 Protocol
定义协议并为不同类型实现,实现多态。
defprotocol 定义;defimpl 实现;@fallback_to_any 兜底。
defprotocol Shape do
def area(shape)
end
defmodule Circle do
defstruct radius: 0
end
defmodule Square do
defstruct side: 0
end
defimpl Shape, for: Circle do
def area(c), do: :math.pi() * c.radius * c.radius
end
defimpl Shape, for: Square do
def area(s), do: s.side * s.side
end
IO.puts("圆面积:#{Float.round(Shape.area(%Circle{radius: 3}), 4)}")
IO.puts("方面积:#{Shape.area(%Square{side: 4})}")
实训7.4 Behaviour 行为
用 @callback 定义行为规范,模块实现回调。
@callback 声明接口;@behaviour 声明遵循;@impl 标记实现。
defmodule Greeter do
@callback greet(name :: String.t()) :: String.t()
end
defmodule PoliteGreeter do
@behaviour Greeter
@impl Greeter
def greet(name), do: "你好,#{name}!"
end
defmodule CasualGreeter do
@behaviour Greeter
@impl Greeter
def greet(name), do: "嗨,#{name}~"
end
IO.puts(PoliteGreeter.greet("小明"))
IO.puts(CasualGreeter.greet("小红"))
单元8 · 错误处理与模式
try/rescue、with、模式匹配错误、raise
- 错误:try/rescue/after。
- with:串联模式匹配验证。
- 抛出:raise ArgumentError。
实训8.1 try/rescue 错误处理
捕获异常并处理,用 after 清理。
try/rescue 捕获;after 类似 ensure。
defmodule Demo do
def safe_div(a, b) do
try do
a / b
rescue
ArithmeticError -> "除数不能为 0"
after
IO.puts("运算完成")
end
end
end
IO.puts(Demo.safe_div(10, 2))
IO.puts(Demo.safe_div(10, 0))
实训8.2 with 管道验证
用 with 串联多个可能失败的模式匹配。
with 依次匹配;失败时执行 else 分支。
defmodule Login do
def validate_user(%{name: name}) when name != "", do: {:ok, name}
def validate_user(_), do: {:error, "用户名为空"}
def validate_age(%{age: age}) when age >= 18, do: {:ok, age}
def validate_age(_), do: {:error, "未成年"}
end
result = with {:ok, name} <- Login.validate_user(%{name: "alice"}),
{:ok, age} <- Login.validate_age(%{age: 25}) do
{:ok, "#{name}(#{age} 岁)登录成功"}
else
{:error, reason} -> {:error, reason}
end
IO.puts(inspect(result))
实训8.3 raise 与 throw
主动抛出异常并捕获。
raise 抛出异常;throw 非局部返回;catch 捕获。
defmodule Demo do
def check_score(score) do
if score < 0 or score > 100 do
raise ArgumentError, "非法分数:#{score}"
else
"分数 #{score} 合法"
end
end
end
for s <- [85, 120] do
try do
IO.puts(Demo.check_score(s))
rescue
e in ArgumentError -> IO.puts("异常:#{e.message}")
end
end
实训8.4 模式匹配错误
演示 match 失败抛 MatchError,用函数子句守卫安全处理。
= 匹配失败抛 MatchError;函数多子句 + 守卫更安全。
defmodule Safe do
def parse_score(s) do
case Integer.parse(s) do
{n, ""} when n >= 0 and n <= 100 -> {:ok, n}
_ -> {:error, "无法解析或越界"}
end
end
end
for s <- ["85", "120", "abc"] do
IO.puts("#{s} -> #{inspect(Safe.parse_score(s))}")
end
单元9 · 递归与列表处理
递归模式、Enum 递归实现、无限流、尾递归
- 递归:[head | tail] 解构。
- 自定义:递归实现 map/filter。
- Stream:惰性序列;unfold 无限流。
实训9.1 递归遍历列表
用递归实现列表求和与长度计算。
[head | tail] 解构 + 基准情形。
defmodule ListDemo do
def sum([]), do: 0
def sum([head | tail]), do: head + sum(tail)
def len([]), do: 0
def len([_ | tail]), do: 1 + len(tail)
end
IO.puts("递归求和:#{ListDemo.sum([1, 2, 3, 4, 5])}")
IO.puts("递归长度:#{ListDemo.len([1, 2, 3, 4, 5])}")
实训9.2 映射与过滤递归
用递归实现 map 与 filter 函数。
自定义高阶递归:map 保留结构、filter 条件筛选。
defmodule MyEnum do
def my_map([], _fun), do: []
def my_map([head | tail], fun), do: [fun.(head) | my_map(tail, fun)]
def my_filter([], _fun), do: []
def my_filter([head | tail], fun) do
if fun.(head), do: [head | my_filter(tail, fun)], else: my_filter(tail, fun)
end
end
IO.puts("my_map 翻倍:#{inspect(MyEnum.my_map([1, 2, 3], &(&1 * 2)))}")
IO.puts("my_filter 偶数:#{inspect(MyEnum.my_filter([1, 2, 3, 4], &(rem(&1, 2) == 0)))}")
实训9.3 Stream 惰性处理
用 Stream 构建惰性序列并取前 n 项。
Stream 惰性求值;Stream.iterate 无限序列;Enum.take 截取。
fib = Stream.unfold({0, 1}, fn {a, b} -> {a, {b, a + b}} end)
IO.puts("斐波那契前 10:#{inspect(Enum.take(fib, 10))}")
nums = Stream.iterate(1, &(&1 + 1))
IO.puts("前 5 个偶数平方:#{inspect(nums |> Stream.filter(&(rem(&1, 2) == 0)) |> Stream.map(&(&1 * &1)) |> Enum.take(5))}")
实训9.4 尾递归性能
用尾递归求 1 到 10000 的和,验证优化。
尾递归用累加器;BEAM 优化尾调用。
defmodule Fast do
def sum_to(n), do: sum_to(n, 0)
defp sum_to(0, acc), do: acc
defp sum_to(n, acc), do: sum_to(n - 1, acc + n)
end
IO.puts("尾递归 1 到 10000 的和:#{Fast.sum_to(10000)}")
单元10 · 并发与进程
spawn、send/receive、Process、Task、Agent
- 进程:spawn 轻量进程。
- 消息:send/receive 通信。
- Task/Agent:异步任务;状态管理。
实训10.1 spawn 进程
用 spawn 创建进程,用 send/receive 通信。
spawn 创建轻量进程;send 发消息;receive 接收。
parent = self()
spawn(fn ->
Process.sleep(100)
send(parent, {:greet, "你好,我是子进程"})
end)
receive do
{:greet, message} -> IO.puts("收到:#{message}")
end
实训10.2 进程间通信
用 send/receive 实现简单的问答通信。
消息模式匹配;超时 receive after。
parent = self()
spawn(fn ->
receive do
{:ask, question} ->
send(parent, {:answer, "你问的是:#{question}"})
end
end)
send(self(), {:ask, "Elixir 好吗?"})
receive do
{:ask, q} -> send(pid(0, 0, 0), {:ask, q})
end
# 简化:直接对已知 pid 通信
pid = spawn(fn ->
receive do
{:ask, question} ->
send(self(), {:answer, "你问的是:#{question}"})
end
end)
send(pid, {:ask, "Elixir 好吗?"})
receive do
{:answer, ans} -> IO.puts("回答:#{ans}")
after
1000 -> IO.puts("超时")
end
实训10.3 Task 异步任务
用 Task.async/await 并行执行并汇总结果。
Task.async 启动;Task.await 等待结果。
task1 = Task.async(fn ->
Process.sleep(100)
10
end)
task2 = Task.async(fn ->
Process.sleep(100)
20
end)
sum = Task.await(task1) + Task.await(task2)
IO.puts("两个任务结果之和:#{sum}")
实训10.4 Agent 状态管理
用 Agent 管理可变状态,实现计数器。
Agent 封装状态;get/update 读取修改。
{:ok, counter} = Agent.start_link(fn -> 0 end)
Agent.update(counter, &(&1 + 1))
Agent.update(counter, &(&1 + 1))
Agent.update(counter, &(&1 + 1))
value = Agent.get(counter, & &1)
IO.puts("计数:#{value}")
单元11 · 宏与元编程
宏基础、quote/unquote、字符串处理综合
- 宏:defmacro 编译期代码转换。
- quote:代码即数据;unquote 注入。
- 字符串:String.split/replace。
实训11.1 宏基础
用 defmacro 定义简单宏,理解代码即数据。
宏在编译期转换代码;quote 表示代码。
defmodule MyMacros do
defmacro unless(condition, do: block) do
quote do
if !unquote(condition), do: unquote(block)
end
end
end
defmodule Demo do
require MyMacros
def check(n) do
MyMacros.unless n > 10 do
"#{n} 不大于 10"
end
end
end
IO.puts(Demo.check(5))
IO.puts(Demo.check(15))
实训11.2 quote 与 unquote
用 quote 查看代码的 AST 表示。
quote 返回 AST;unquote 在宏内注入值。
ast = quote do
1 + 2 * 3
end
IO.puts("AST:#{inspect(ast)}")
num = 42
ast2 = quote do
unquote(num) + 1
end
IO.puts("unquote AST:#{inspect(ast2)}")
IO.puts("求值:#{Code.eval_quoted(ast2) |> elem(0)}")
实训11.3 字符串综合处理
用 String 模块做拆分、替换、模式匹配综合处理。
String.split/replace/reverse;正则 ~r。
text = "elixir is powerful, elixir is concurrent, elixir is fun"
words = String.split(text, ~r/[,\s]+/, trim: true)
IO.puts("单词数:#{length(words)}")
freq = Enum.frequencies(words)
IO.puts("词频:#{inspect(freq)}")
replaced = String.replace(text, "elixir", "Elixir")
IO.puts("替换后:#{replaced}")
IO.puts("大写:#{String.upcase(String.slice(text, 0, 10))}")
实训11.4 宏应用案例
用宏定义重复执行 n 次的语法结构。
宏可以创造新的语法结构。
defmodule Times do
defmacro n_times(n, do: block) do
quote do
Enum.each(1..unquote(n), fn _ -> unquote(block) end)
end
end
end
defmodule Demo do
require Times
def run do
Times.n_times 3 do
IO.puts("重复执行!")
end
end
end
Demo.run()
单元12 · 综合项目实训
综合运用 Elixir 知识完成项目
- 综合:struct、Enum、Task、模式匹配组合。
- 工程化:sort_by、frequencies、管道。
实训12.1 学生成绩统计
用结构体与 Enum 处理学生成绩,输出平均与排名。
defstruct + Enum 组合 + sort_by 排序。
defmodule Student do
defstruct name: "", chinese: 0.0, math: 0.0
def average(s), do: (s.chinese + s.math) / 2
end
students = [
%Student{name: "张三", chinese: 90, math: 85},
%Student{name: "李四", chinese: 78, math: 92},
%Student{name: "王五", chinese: 88, math: 76}
]
sorted = Enum.sort_by(students, &(-Student.average(&1)))
IO.puts("姓名\t语文\t数学\t平均")
Enum.each(sorted, fn s ->
IO.puts("#{s.name}\t#{s.chinese}\t#{s.math}\t#{Float.round(Student.average(s), 1)}")
end)
avg = Enum.sum(Enum.map(students, &Student.average(&1))) / length(students)
IO.puts("全班平均:#{Float.round(avg, 1)}")
IO.puts("第一名:#{hd(sorted).name}")
实训12.2 词频统计器
统计文本单词出现次数,输出出现最多的单词。
String.split + Enum.frequencies + max_by。
text = "elixir is functional elixir is concurrent elixir is fun"
words = String.split(text)
freq = Enum.frequencies(words)
Enum.sort_by(freq, fn {_, c} -> -c end)
|> Enum.each(fn {w, c} -> IO.puts("#{w}:#{c}") end)
top = Enum.max_by(freq, fn {_, c} -> c end)
IO.puts("出现最多:#{elem(top, 0)},#{elem(top, 1)} 次")
实训12.3 并发下载模拟
用 Task 并发模拟多个下载任务并汇总。
Task.async + await + Enum.map 并发。
download = fn name ->
Process.sleep(200)
"#{name} 下载完成"
end
files = ["文件1", "文件2", "文件3", "文件4"]
tasks = Enum.map(files, fn f -> Task.async(fn -> download.(f) end) end)
results = Enum.map(tasks, &Task.await/1)
Enum.each(results, &IO.puts/1)
IO.puts("全部完成:#{length(results)} 个任务")
实训12.4 简易计算器
实现支持 + - * / 的计算器:解析表达式并计算。
String.split 解析 + case 分发 + 模式匹配。
defmodule Calc do
def calculate(expr) do
parts = String.split(expr)
case parts do
[a, op, b] ->
{num_a, ""} = Integer.parse(a)
{num_b, ""} = Integer.parse(b)
case op do
"+" -> {:ok, num_a + num_b}
"-" -> {:ok, num_a - num_b}
"*" -> {:ok, num_a * num_b}
"/" when num_b != 0 -> {:ok, num_a / num_b}
"/" -> {:error, "除数不能为 0"}
_ -> {:error, "不支持的运算符:#{op}"}
end
_ -> {:error, "格式错误"}
end
end
end
for expr <- ["10 + 5", "10 * 3", "10 / 0", "abc + 1"] do
case Calc.calculate(expr) do
{:ok, v} -> IO.puts("#{expr} = #{v}")
{:error, e} -> IO.puts("#{expr} -> 错误:#{e}")
end
end