流程控制
Julia 提供了大量的流程控制构件:
- 复合表达式:begin 和 ;
- 条件表达式:if-elseif-else 和 ?: (三元运算符)
- 短路求值:&&、|| 和链式比较
- 重复执行:循环:while 和 for
- 异常处理:try-catch、error 和 throw
- Task(协程):yieldto
前五个流程控制机制是高级编程语言的标准。Task 不是那么的标准:它提供了非局部的流程控制,这使得在暂时挂起的计算任务之间进行切换成为可能。这是一个功能强大的构件:Julia 中的异常处理和协同多任务都是通过 Task 实现的。
复合表达式
begin-end
julia> z = begin
x = 1
y = 2
x + y
end
3
;链
julia> z = (x = 1; y = 2; x + y)
3
条件表达式
if-elseif-else可以根据布尔表达式的值,让部分代码被执行或者不被执行。
if x < y
println("x is less than y")
elseif x > y
println("x is greater than y")
else
println("x is equal to y")
end
Note
-
if 代码块是"有渗漏的",也就是说它们不会引入局部作用域。这意味着在 if 语句中新定义的变量依然可以在 if 代码块之后使用,尽管这些变量没有在 if 语句之前定义过。然而,在利用这种行为的时候,要保证变量在所有的分支下都进行了定义。
-
if 代码块也会返回一个值,这可能对于一些从其他语言转过来的用户来说不是很直观。 这个返回值就是被执行的分支中最后一个被执行的语句的返回值。
短路求值
在一系列由这些运算符连接的布尔表达式中,为了得到整个链的最终布尔值,仅仅只有最小数量的表达式被计算。i.e.
- 在表达式 a && b 中,子表达式 b 仅当 a 为 true 的时候才会被执行。
- 在表达式 a || b 中,子表达式 b 仅在 a 为 false 的时候才会被执行。
- && 或者 || 的操作数必须是布尔值(true 或者 false)。在链式嵌套的条件表达式中, 除最后一项外,使用非布尔值会导致错误,但在链的末尾允许使用任意类型的表达式。
循环
while
julia> i = 1;
julia> while i <= 5
println(i)
global i += 1
end
for
julia> for i = 1:5
println(i)
end
一般来说,for 循环组件可以用于迭代任一个容器。在这种情况下,相比 =,另外的(但完全相同)关键字 in 或者 ∈ 则更常用,因为它使得代码更清晰。
for i in "Hello World"
for i in [1, 2, 3, 4, 5]
数组推导:
julia> [x^2 for x in 1:4]
4-element Vector{Int64}:
1
4
9
16
多个嵌套的 for 循环可以合并到一个外部循环,可以用来创建其迭代对象的笛卡尔积:
julia> for i = 1:2, j = 3:4
println((i, j))
end
(1, 3)
(1, 4)
(2, 3)
(2, 4)
异常处理
try-catch
try
代码块
catch err
if isa(err, <错误类型1>)
处理错误
elseif isa(err, <错误类型2>)
处理错误
...
finally
总是会被执行,用来释放资源等
- 可以用 throw 显式地创建异常。
- 可以用 error 函数生成一个 ErrorException 来中断正常的控制流程。
任务 / 协程
也被称为对称协程,轻量级线程,协同多任务等。如果一个计算以任务方式执行,就很可能被其他任务中断。原先任务恢复后会从被中断的地方继续工作。
与函数的区别:
- 任务切换不需要空间,可以完成任意数量任务的切换而无需考虑调用栈
- 任务切换可以按照任何顺序进行
任务适合生产者-消费者模式。一个过程生产值,另一个消费值。消费者不能简单调用生产者得到值,因为二者执行时间不一定协同。
Channel:解决生产者-消费者问题,底层是FIFO队列,用put!和take!实现。
# producer
function producer(c::Channel)
put!(c, "start")
for n = 1:4
put(c, 2n)
end
put(c, "end")
end
# customer
for x in Channel(producer)
println(x)
end
Note. 两次调用put!之间,生产者的执行是挂起的,由消费者接管控制。当任务结束时,Channel对象会自动关闭。