你已经了解了如何逐个构建数组和字典等集合,或者使用函数来生成它们。现在,我们来看一种强大且简洁的集合创建方法:推导式。推导式提供了一种紧凑的语法,可以从现有可迭代对象(如范围或其他集合)生成集合,这通常使你的代码更具可读性和表现力,尤其是在创建新集合的逻辑直接明了时。可以把推导式看作是一种常见模式的简写:对一些值进行迭代,对每个值执行操作,然后将结果收集到一个新集合中。数组推导式:即时构建数组数组推导式可能是你最常遇到的类型。它们允许你在一行可读的代码中,根据一个表达式和一个输入序列来定义数组的内容。数组推导式的基本语法是: [expression for variable in iterable]让我们分解一下:expression:这是新数组中每个元素将计算的值。它通常会用到 variable。variable:这是一个占位符,它会逐一取用 iterable 中每个项的值。iterable:这是任何可以被循环的对象,例如一个范围(1:5)、一个现有数组或一个字符串。包裹语法的方括号 [] 表示正在创建一个 Array。例如,如果我们想创建一个包含前五个正整数平方的数组,与其编写循环并使用 push! 将元素添加到数组中,不如使用推导式:julia> squares = [i*i for i in 1:5] 5-element Vector{Int64}: 1 4 9 16 25在这里,i 取值从 1 到 5,i*i 被计算,结果成为 squares 数组中的一个元素。使用 if 进行筛选推导式还可以包含 if 子句,以便在应用表达式之前从可迭代对象中筛选项。语法变为: [expression for variable in iterable if condition]只有 condition 评估为 true 的项才会被 expression 处理并包含在结果数组中。假设我们想要一个平方数组,但只包含 1 到 10 之间的偶数:julia> even_squares = [x^2 for x in 1:10 if x % 2 == 0] 5-element Vector{Int64}: 4 16 36 64 100在这个例子中,x 从 1 迭代到 10。if x % 2 == 0 条件检查 x 是否为偶数。如果是,则计算 x^2 并将其添加到 even_squares 数组中。如果 Julia 的自动类型推断不是你所需要的,你也可以指定结果数组的元素类型:julia> float_halves = Float64[i/2 for i in 1:5] 5-element Vector{Float64}: 0.5 1.0 1.5 2.0 2.5这确保了 float_halves 是一个 Float64 值的数组。字典推导式:轻松构建字典与数组推导式类似,字典推导式提供了一种简洁的创建字典的方法。语法包括为可迭代对象中的每个项指定一个键值对。一般形式是: Dict(key_expression => value_expression for variable in iterable)或者带条件时: Dict(key_expression => value_expression for variable in iterable if condition)让我们创建一个字典,它的键是 1 到 5 的数字,值是它们的立方:julia> number_to_cube = Dict(i => i^3 for i in 1:5) Dict{Int64, Int64} with 5 entries: 5 => 125 4 => 64 2 => 8 3 => 27 1 => 1在这里,对于从 1 到 5 的每个 i,都会创建一个 i => i^3 对并添加到字典中。你也可以通过使用 zip 同时迭代多个集合来创建字典,zip 会将两个或更多可迭代对象中对应的元素配对。julia> names = ["Alice", "Bob", "Charlie"]; julia> ages = [30, 24, 35]; julia> name_to_age = Dict(name => age for (name, age) in zip(names, ages)) Dict{String, Int64} with 3 entries: "Bob" => 24 "Alice" => 30 "Charlie" => 35在这种情况下,(name, age) 是一个元组,它从 zip(names, ages) 生成的每对中解包出来。生成器表达式:推导式背后的机制如果你不想立即创建一个完整的数组或字典,而是想要一个可以按需生成值的对象,该怎么办?这就是生成器表达式的作用。它们看起来与数组推导式非常相似,但没有外部的方括号。语法是: (expression for variable in iterable if condition)这会创建一个 生成器。生成器是可迭代对象。它不会一次性计算所有值并存储在内存中。相反,它会按需逐个计算值。对于大型序列,这可以非常节省内存。julia> gen = (i * 10 for i in 1:5) Base.Generator{UnitRange{Int64}, var"#5#6"}(var"#5#6"(), 1:5) julia> for val in gen println(val) end 10 20 30 40 50注意,打印 gen 本身并不会显示值,而是显示一个代表生成器的对象。值是在你迭代它时生成的。使用生成器构建其他集合这些生成器表达式特别有用,因为它们可以传递给集合构造函数来构建元组 (Tuples)、集合 (Sets),甚至数组和字典(如果你喜欢这种两步法,尽管直接推导式对于数组和字典通常更符合习惯)。创建元组要使用类似推导式的语法创建元组,你可以在 Tuple 构造函数中使用生成器表达式:julia> odd_numbers_tuple = Tuple(x for x in 1:10 if x % 2 != 0) (1, 3, 5, 7, 9)创建集合同样,对于存储唯一项的集合:julia> unique_chars = Set(c for c in "hello world!" if isletter(c)) Set{Char} with 10 entries: 'r' 'e' 'h' 'w' 'l' 'o' 'd'即使 'l' 和 'o' 在 "hello world!" 中多次出现,它们在集合中也只出现一次,并且由于 if isletter(c) 条件,只有字母被包含在内。推导式和生成器表达式是 Julia 中富有表现力的代码的标志。它们让你能清楚地表达 要构建什么(你想构建的集合),而不是 如何构建(显式的循环和追加机制),从而在你熟悉它们的结构后,编写出更简洁、通常也更易读的代码。它们在 Julia 程序中很常见,所以理解它们对于编写自己的代码和阅读他人的代码都非常重要。