Julia 教程 / 控制流与异常处理
控制流与异常处理
控制流速查表
| 语法 | 用途 | 示例 |
|---|---|---|
if/elseif/else | 条件分支 | if x > 0 ... end |
?: | 三元运算符 | x > 0 ? "pos" : "neg" |
&& / ` | ` | |
for ... in | 循环迭代 | for i in 1:10 ... end |
while | 条件循环 | while cond ... end |
break | 跳出循环 | if done break end |
continue | 跳过迭代 | if skip continue end |
try/catch/finally | 异常处理 | try ... catch e ... end |
throw | 抛出异常 | throw(Error("msg")) |
@assert | 断言检查 | @assert x > 0 |
1. 条件判断
if / elseif / else
function classify_number(n)
if n > 0
return "正数"
elseif n < 0
return "负数"
else
return "零"
end
end
classify_number(5) # "正数"
classify_number(-3) # "负数"
classify_number(0) # "零"
⚠️ 注意: Julia 的
if是表达式(expression),可以返回值。最后执行的分支的值就是if表达式的值。
# if 作为表达式赋值
x = if true
42
else
0
end
# x = 42
# 简化写法(单行)
status = if age >= 18 "adult" else "minor" end
三元运算符 ? :
# 语法:条件 ? 真值 : 假值
x = 10
label = x > 0 ? "positive" : "non-positive"
# 嵌套(不推荐过度嵌套)
grade = score >= 90 ? "A" :
score >= 80 ? "B" :
score >= 70 ? "C" :
score >= 60 ? "D" : "F"
# 三元运算符也是表达式,可以内联
println(x > 0 ? "$x is positive" : "$x is non-positive")
短路运算 && 和 ||
# && — 逻辑与(短路)
# 如果左边为 false,右边不执行
x > 0 && println("x is positive")
# || — 逻辑或(短路)
# 如果左边为 true,右边不执行
x < 0 || println("x is non-negative")
# 常见模式:默认值
name = user_input || "Anonymous"
# 常见模式:前置条件检查
function divide(a, b)
b != 0 || error("Division by zero")
return a / b
end
# 复杂短路表达式
is_valid(x) = x !== nothing && x > 0 && x < 100
💡 提示: 短路运算在 Julia 中常用来替代简单的
if语句,使代码更简洁。但过度使用会降低可读性。
2. for 循环
范围迭代
# 基本范围
for i in 1:5
println(i) # 1, 2, 3, 4, 5
end
# 带步长
for i in 1:2:10
println(i) # 1, 3, 5, 7, 9
end
# 逆序
for i in 10:-1:1
println(i) # 10, 9, 8, ..., 1
end
# 浮点范围
for x in 0.0:0.1:1.0
println(x)
end
集合迭代
# 数组
fruits = ["apple", "banana", "cherry"]
for fruit in fruits
println(fruit)
end
# 字典
scores = Dict("Alice" => 95, "Bob" => 87, "Charlie" => 92)
for (name, score) in scores
println("$name: $score")
end
# 字符串(逐字符)
for c in "Hello"
print("$c ")
end
# H e l l o
# 使用 eachindex 获取索引
for i in eachindex(fruits)
println("Index $i: $(fruits[i])")
end
嵌套循环
# 多重循环
for i in 1:3, j in 1:3
println("($i, $j)")
end
# 等价写法
for i in 1:3
for j in 1:3
println("($i, $j)")
end
end
# 生成乘法表
for i in 1:9, j in 1:i
print("$(j)×$(i)=$(i*j)\t")
if j == i
println()
end
end
高级迭代
# enumerate — 带索引迭代
for (i, val) in enumerate(["a", "b", "c"])
println("$i: $val")
end
# 1: a
# 2: b
# 3: c
# zip — 并行迭代
names = ["Alice", "Bob", "Charlie"]
ages = [30, 25, 35]
for (name, age) in zip(names, ages)
println("$name is $age years old")
end
# pairs — 键值对迭代
for (k, v) in pairs(Dict("a" => 1, "b" => 2))
println("$k => $v")
end
3. while 循环
# 基本 while
n = 1
while n <= 100
global n *= 2
end
println(n) # 128
# 条件查找
function find_root(f, x0; tol=1e-8, maxiter=1000)
x = x0
for i in 1:maxiter
fx = f(x)
abs(fx) < tol && return x
x -= fx / (f(x + tol) - fx) * tol # 简化牛顿法
end
return x
end
# while 中的 break
count = 0
while true
count += 1
if count > 10
break
end
end
4. break 和 continue
# break — 跳出循环
for i in 1:100
if i > 10
break
end
println(i)
end
# continue — 跳过当前迭代
for i in 1:10
if i % 3 == 0
continue
end
println(i) # 1, 2, 4, 5, 7, 8, 10
end
# break 和 continue 在嵌套循环中只影响最内层
for i in 1:3
for j in 1:3
if j == 2
break # 只跳出内层循环
end
println("($i, $j)")
end
end
⚠️ 注意: Julia 没有
break n语法来跳出多层嵌套循环。可以使用@goto/@label或将嵌套循环封装为函数,通过return提前退出。
5. 列表推导式
# 基本推导式
squares = [x^2 for x in 1:10]
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 带条件过滤
evens = [x for x in 1:20 if x % 2 == 0]
# [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
# 多重迭代
matrix = [i * j for i in 1:3, j in 1:3]
# 3×3 Matrix{Int64}:
# 1 2 3
# 2 4 6
# 3 6 9
# 生成器表达式(惰性求值,不分配内存)
sum(x^2 for x in 1:100) # 338350
# 嵌套推导式
nested = [[i*j for j in 1:3] for i in 1:3]
# [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
# 字典推导式
d = Dict("$i" => i^2 for i in 1:5)
# Dict("5" => 25, "4" => 16, "2" => 4, "3" => 9, "1" => 1)
# 集合推导式
s = Set(x % 3 for x in 1:10)
# Set([0, 1, 2])
6. try / catch / finally
基本异常处理
# 捕获特定异常
try
result = 1 / 0
catch e
if e isa DivideError
println("不能除以零")
else
println("其他错误: $e")
end
end
# 捕获所有异常
try
parse(Int, "not a number")
catch e
println("解析失败: $(sprint(showerror, e))")
end
# finally 块(无论是否异常都执行)
function safe_read(filename)
io = nothing
try
io = open(filename)
return read(io, String)
catch e
println("读取失败: $e")
return nothing
finally
if io !== nothing
close(io)
end
println("清理完成")
end
end
# 多重 catch(Julia 1.7+)
try
# 某些操作
catch e::BoundsError
println("越界错误")
catch e::MethodError
println("方法错误")
catch e
println("其他错误: $e")
end
7. throw 与自定义异常
抛出异常
# 抛出内置异常
throw(DomainError(-1, "参数必须为正数"))
throw(ArgumentError("无效参数"))
throw(ErrorException("一般性错误"))
# 条件抛出
function sqrt_positive(x)
x < 0 && throw(DomainError(x, "负数不能开平方"))
return sqrt(x)
end
sqrt_positive(4) # 2.0
sqrt_positive(-1) # DomainError: 负数不能开平方
自定义异常类型
# 自定义异常类型
struct InsufficientFundsError <: Exception
balance::Float64
amount::Float64
end
# 自定义显示
function Base.showerror(io::IO, e::InsufficientFundsError)
print(io, "InsufficientFundsError: 余额不足 (余额: \$$(e.balance), 取款: \$$(e.amount))")
end
# 使用自定义异常
function withdraw(balance, amount)
amount > balance && throw(InsufficientFundsError(balance, amount))
return balance - amount
end
try
withdraw(100.0, 150.0)
catch e
showerror(stdout, e)
# InsufficientFundsError: 余额不足 (余额: $100.0, 取款: $150.0)
end
8. @assert 宏
# 基本断言
@assert 1 == 1 # 通过
@assert 1 == 2 "1 不等于 2" # AssertionError: 1 不等于 2
# 使用场景:函数参数验证
function create_user(name, age)
@assert !isempty(name) "用户名不能为空"
@assert age >= 0 && age <= 150 "年龄必须在 0-150 之间"
return (name=name, age=age)
end
create_user("Alice", 30) # OK
create_user("", 30) # AssertionError: 用户名不能为空
# @assert 可以被禁用(生产环境)
# 使用 --check-bounds=no 启动 Julia 时,@assert 不会执行
⚠️ 注意:
@assert不适合用于验证用户输入或外部数据。它用于编程逻辑的内部一致性检查。对外部数据应使用throw。
9. 其他控制流
复合表达式 begin / end
# 将多个表达式组合为一个块
x = begin
a = 10
b = 20
a + b
end
# x = 30
let 块
# 创建新的作用域
let x = 10
x = x + 1 # x 是局部变量
println(x) # 11
end
# println(x) # ERROR: x 未定义
# 在闭包中使用 let 捕获变量
funcs = []
for i in 1:3
let i = i
push!(funcs, () -> println(i))
end
end
# funcs[1]() → 1
# funcs[2]() → 2
# funcs[3]() → 3
ifelse 函数
# 向量化的条件选择(不是 if/else)
x = [1, -2, 3, -4, 5]
result = ifelse.(x .> 0, x, 0)
# [1, 0, 3, 0, 5]
# 与 if/else 的区别:
# ifelse 是函数,对两个分支都要求值
# if/else 是控制流,只求值被选中的分支
return 与 nothing
# 无返回值的函数默认返回 nothing
function print_hello()
println("Hello!")
# 隐式返回 nothing
end
result = print_hello()
# Hello!
result === nothing # true
# 提前返回
function find_first_negative(xs)
for x in xs
if x < 0
return x
end
end
return nothing # 未找到
end
10. 综合业务场景:命令行计算器
function calculator()
println("=== Julia 计算器 (输入 q 退出) ===")
history = Float64[]
while true
print("> ")
input = readline()
# 退出条件
(input == "q" || input == "quit") && break
# 跳过空输入
isempty(input) && continue
try
# 解析表达式(简单版本:两个操作数一个运算符)
parts = split(input)
if length(parts) != 3
println("格式: 数字 运算符 数字 (如: 3 + 4)")
continue
end
a = parse(Float64, parts[1])
op = parts[2][1]
b = parse(Float64, parts[3])
result = if op == '+'
a + b
elseif op == '-'
a - b
elseif op == '*'
a * b
elseif op == '/'
b == 0 ? throw(DivideError()) : a / b
elseif op == '^'
a ^ b
else
throw(ArgumentError("不支持的运算符: $op"))
end
println("= $result")
push!(history, result)
catch e
if e isa ParseError
println("解析错误: 请输入有效的数字")
elseif e isa DivideError
println("错误: 不能除以零")
elseif e isa ArgumentError
println("错误: $(e.msg)")
else
println("未知错误: $e")
end
end
end
if !isempty(history)
println("\n计算历史: ", join(history, ", "))
println("总和: ", sum(history))
end
println("再见!")
end
# 运行计算器
calculator()
扩展阅读
- Julia 官方文档 — Control Flow
- Julia 官方文档 — Exception Handling
- Julia 官方文档 — Variables and Scoping
- Julia 变量作用域规则详解
- Julia 列表推导式最佳实践
📌 本章小结: Julia 的
if是表达式(可返回值),支持三元运算符和短路运算。for支持范围、集合、enumerate、zip 等迭代方式。异常处理使用try/catch/finally,支持自定义异常类型。@assert用于内部一致性检查。let创建新的作用域,ifelse是向量化的条件函数。