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

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 是控制流,只求值被选中的分支

returnnothing

# 无返回值的函数默认返回 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 的 if 是表达式(可返回值),支持三元运算符和短路运算。for 支持范围、集合、enumerate、zip 等迭代方式。异常处理使用 try/catch/finally,支持自定义异常类型。@assert 用于内部一致性检查。let 创建新的作用域,ifelse 是向量化的条件函数。