与其他语言的显著差异
与 MATLAB 的显著差异
虽然 MATLAB 用户可能会发现 Julia 的语法很熟悉,但 Julia 不是 MATLAB 的克隆。 它们之间存在重大的语法和功能差异。 以下是一些可能会使习惯于 MATLAB 的 Julia 用户感到困扰的显著差异:
- Julia 数组使用方括号
A[i,j]
进行索引。 Julia 数组在分配给另一个变量时不会被复制。 在
A = B
之后,改变B
的元素也会改变A
的元素。Julia 的值在向函数传递时不发生复制。如果某个函数修改了数组,这一修改对调用者是可见的。
Julia 不会在赋值语句中自动增长数组。 而在 MATLAB 中
a(4) = 3.2
可以创建数组a = [0 0 0 3.2]
,而a(5) = 7
可以将它增长为a = [0 0 0 3.2 7]
。如果a
的长度小于 5 或者这个语句是第一次使用标识符a
,则相应的 Julia 语句a[5] = 7
会抛出错误。Julia 使用push!
和append!
来增长Vector
,它们比 MATLAB 的a(end+1) = val
更高效。
- 虚数单位
sqrt(-1)
在 Julia 中表示为im
,而不是在 MATLAB 中的i
或j
。 - 在 Julia 中,没有小数点的数字字面量(例如
42
)会创建整数而不是浮点数。也支持任意大整数字面量。因此,某些操作(如2^-1
)将抛出 domain error,因为结果不是整数(有关的详细信息,请参阅常见问题中有关 domain errors 的条目)。 在 Julia 中,没有小数点的数字字面量(例如42
)会创建整数而不是浮点数。因此,某些操作会因为需要浮点数而抛出 domain error;例如julia > a = -1; 2^a
,因为结果不是整数了。请参阅常见问题中有关 domain errors 的条目)。
- 在 Julia 中,能返回多个值并将其赋值为元组,例如
(a, b) = (1, 2)
或a, b = 1, 2
。 在 Julia 中不存在 MATLAB 的nargout
,它通常在 MATLAB 中用于根据返回值的数量执行可选工作。取而代之的是,用户可以使用可选参数和关键字参数来实现类似的功能。
Julia 拥有真正的一维数组。列向量的大小为
N
,而不是Nx1
。例如,rand(N)
创建一个一维数组。在 Julia 中,
[x,y,z]
将始终构造一个包含x
、y
和z
的 3 元数组。要在第一个维度(「垂直列」)中连接元素,请使用
vcat(x,y,z)
或用分号分隔([x; y; z]
)。要在第二个维度(「水平行」)中连接元素,请使用
hcat(x,y,z)
或用空格分隔([x y z]
)。要构造分块矩阵(在前两个维度中连接元素),请使用
hvcat
或组合空格和分号([a b; c d]
)。
在 Julia 中,
a:b
和a:b:c
构造AbstractRange
对象。使用collect(a:b)
构造一个类似 MATLAB 中完整的向量。通常,不需要调用collect
。在大多数情况下,AbstractRange
对象将像普通数组一样运行,但效率更高,因为它是懒惰求值。这种创建专用对象而不是完整数组的模式经常被使用,并且也可以在诸如range
之类的函数中看到,或者在诸如enumerate
和zip
之类的迭代器中看到。特殊对象大多可以像正常数组一样使用。
- Julia 中的函数返回其最后一个表达式或
return
关键字的值而无需在函数定义中列出要返回的变量的名称(有关详细信息,请参阅 return 关键字)。
- Julia 脚本可以包含任意数量的函数,并且在加载文件时,所有定义都将在外部可见。可以从当前工作目录之外的文件加载函数定义。
- 在 Julia 中,调用无参数的函数时必须使用小括号,例如
rand()
。 - Julia 不鼓励使用分号来结束语句。语句的结果不会自动打印(除了在 REPL 中),并且代码的一行不必使用分号结尾。
println
或者@printf
能用来打印特定输出。
- 在 Julia 中,运算符
&
、|
和⊻
(xor
)进行按位操作,分别与MATLAB中的and
、or
和xor
等价,并且优先级与 Python 的按位运算符相似(不像 C)。他们可以对标量运算或者数组中逐元素运算,可以用来合并逻辑数组,但是注意运算顺序的区别:括号可能是必要的(例如,选择A
中等于 1 或 2 的元素可使用(A .== 1) .| (A .== 2)
)。
在 Julia 中,集合的元素可以使用 splat 运算符
...
来作为参数传递给函数,如xs=[1,2]; f(xs...)
。Julia 的
svd
将奇异值作为向量而非密集对角矩阵返回。在 Julia 中,
...
不用于延续代码行。不同的是,Julia 中不完整的表达式会自动延续到下一行。在 Julia 和 MATLAB 中,变量
ans
被设置为交互式会话中提交的最后一个表达式的值。在 Julia 中与 MATLAB 不同的是,当 Julia 代码以非交互式模式运行时并不会设置ans
。
- Julia 的
struct
不支持在运行时动态地添加字段,这与 MATLAB 的class
不同。 如需支持,请使用Dict
。Julia 中的字典不是有序的。 在 Julia 中,每个模块有自身的全局作用域/命名空间,而在 MATLAB 中只有一个全局作用域。
在 MATLAB 中,删除不需要的值的惯用方法是使用逻辑索引,如表达式
x(x>3)
或语句x(x>3) = []
来 in-place 修改x
。相比之下,Julia 提供了更高阶的函数filter
和filter!
,允许用户编写filter(z->z>3, x)
和filter!(z->z>3, x)
来代替相应直译x[x.>3]
和x = x[x.>3]
。使用filter!
可以减少临时数组的使用。
类似于提取(或「解引用」)元胞数组的所有元素的操作,例如 MATLAB 中的
vertcat(A{:})
,在 Julia 中是使用 splat 运算符编写的,例如vcat(A...)
。在 Julia 中,
adjoint
函数执行共轭转置;在 MATLAB 中,adjoint
提供了经典伴随,它是余子式的转置。在 Julia 中,a^b^c 被认为是 a^(b^c) 而在 MATLAB 中它是 (a^b)^c。
与 R 的显著差异
Julia 的目标之一是为数据分析和统计编程提供高效的语言。对于从 R 转到 Julia 的用户来说,这是一些显著差异:
- Julia 的单引号封闭字符,而不是字符串。
Julia 可以通过索引字符串来创建子字符串。在 R 中,在创建子字符串之前必须将字符串转换为字符向量。
在 Julia 中,与 Python 相同但与 R 不同的是,字符串可由三重引号
""" ... """
创建。此语法对于构造包含换行符的字符串很方便。在 Julia 中,可变参数使用 splat 运算符
...
指定,该运算符总是跟在具体变量的名称后面,与 R 的不同,R 的...
可以单独出现。在 Julia 中,模数是
mod(a, b)
,而不是a %% b
。Julia 中的%
是余数运算符。- 在 Julia 中,并非所有数据结构都支持逻辑索引。此外,Julia 中的逻辑索引只支持长度等于被索引对象的向量。例如:
* 在 R 中,`c(1, 2, 3, 4)[c(TRUE, FALSE)]` 等价于 `c(1, 3)`。
* 在 R 中,`c(1, 2, 3, 4)[c(TRUE, FALSE, TRUE, FALSE)]` 等价于 `c(1, 3)`。
* 在 Julia 中,`[1, 2, 3, 4][[true, false]]` 抛出 [`BoundsError`]($zh_CN-doc-src-manual-@ref)。
* 在 Julia 中,`[1, 2, 3, 4][[true, false, true, false]]` 产生 `[1, 3]`。
- 与许多语言一样,Julia 并不总是允许对不同长度的向量进行操作,与 R 不同,R 中的向量只需要共享一个公共的索引范围。例如,
c(1, 2, 3, 4) + c(1, 2)
是有效的 R,但等价的[1, 2, 3, 4] + [1, 2]
在 Julia 中会抛出一个错误。
- 在逗号不改变代码含义时,Julia 允许使用可选的尾随括号。在索引数组时,这可能在 R 用户间造成混淆。例如,R 中的
x[1,]
将返回矩阵的第一行;但是,在 Julia 中,引号被忽略,于是x[1,] == x[1]
,并且将返回第一个元素。要提取一行,请务必使用:
,如x[1,:]
。
- Julia 的
map
首先接受函数,然后是该函数的参数,这与 R 中的lapply(<structure>, function, ...)
不同。类似地,R 中的apply(X, MARGIN, FUN, ...)
等价于 Julia 的mapslices
,其中函数是第一个参数。
R 中的多变量 apply,如
mapply(choose, 11:13, 1:3)
,在 Julia 中可以编写成broadcast(binomial, 11:13, 1:3)
。等价地,Julia 提供了更短的点语法来向量化函数binomial.(11:13, 1:3)
。Julia 使用
end
来表示条件块(如if
)、循环块(如while
/for
)和函数的结束。为了代替单行if ( cond ) statement
,Julia 允许形式为if cond; statement; end
、cond && statement
和!cond || statement
的语句。后两种语法中的赋值语句必须显式地包含在括号中,例如cond && (x = value)
,这是因为运算符的优先级。
- 在 Julia 中,
<-
,<<-
和->
不是赋值运算符。 - Julia 的
->
创建一个匿名函数。 - Julia 使用括号构造向量。Julia 的
[1, 2, 3]
等价于 R 的c(1, 2, 3)
。 - Julia 的
*
运算符可以执行矩阵乘法,这与 R 不同。如果A
和B
都是矩阵,那么A * B
在 Julia 中表示矩阵乘法,等价于 R 的A %*% B
。在 R 中,相同的符号将执行逐元素(Hadamard)乘积。要在 Julia 中使用逐元素乘法运算,你需要编写A .* B
。
- Julia 使用
transpose
函数来执行矩阵转置,使用'
运算符或adjoint
函数来执行共轭转置。因此,Julia 的transpose(A)
等价于 R 的t(A)
。另外,Julia 中的非递归转置由permutedims
函数提供。
Julia 在编写
if
语句或for
/while
循环时不需要括号:请使用for i in [1, 2, 3]
代替for (int i=1; i <= 3; i++)
,以及if i == 1
代替if (i == 1)
Julia 不把数字
0
和1
视为布尔值。在 Julia 中不能编写if (1)
,因为if
语句只接受布尔值。相反,可以编写if true
、if Bool(1)
或if 1==1
。
Julia 不提供
nrow
和ncol
。相反,请使用size(M, 1)
代替nrow(M)
以及size(M, 2)
代替ncol(M)
Julia 仔细区分了标量、向量和矩阵。在 R 中,
1
和c(1)
是相同的。在 Julia 中,它们不能互换地使用。Julia 赋值操作的左侧不能为函数调用的结果:你不能编写
diag(M) = fill(1, n)
。Julia 不鼓励使用函数填充主命名空间。Julia 的大多数统计功能都可在 JuliaStats 组织的包中找到。例如:
* 与概率分布相关的函数由 [Distributions 包](https://github.com/JuliaStats/Distributions.jl)提供。
* [DataFrames 包](https://github.com/JuliaData/DataFrames.jl)提供数据帧。
* 广义线性模型由 [GLM 包](https://github.com/JuliaStats/GLM.jl)提供。
- Julia 提供了元组和真正的哈希表,但不提供 R 风格的列表。在返回多个项时,通常应使用元组或具名元组:请使用
(1, 2)
或(a=1, b=2)
代替list(a = 1, b = 2)
。
- Julia 鼓励用户编写自己的类型,它比 R 中的 S3 或 S4 对象更容易使用。Julia 的多重派发系统意味着
table(x::TypeA)
和table(x::TypeB)
类似于 R 的table.TypeA(x)
和table.TypeB(x)
。
- Julia 的值在向函数传递时不发生复制。如果某个函数修改了数组,这一修改对调用者是可见的。这与 R 非常不同,允许新函数更高效地操作大型数据结构。
在 Julia 中,向量和矩阵使用
hcat
、vcat
和hvcat
拼接,而不是像在 R 中那样使用c
、rbind
和cbind
。在 Julia 中,像
a:b
这样的 range 不是 R 中的向量简写,而是一个专门的AbstractRange
对象,该对象用于没有高内存开销地进行迭代。要将 range 转换为 vector,请使用collect(a:b)
。
- Julia 的
max
和min
分别等价于 R 中的pmax
和pmin
,但两者的参数都需要具有相同的维度。虽然maximum
和minimum
代替了 R 中的max
和min
,但它们之间有重大区别。
- Julia 的
sum
、prod
、maximum
和minimum
与它们在 R 中的对应物不同。它们都接受一个可选的关键字参数dims
,它表示执行操作的维度。例如,在 Julia 中令A = [1 2; 3 4]
,在 R 中令B <- rbind(c(1,2),c(3,4))
是与之相同的矩阵。然后sum(A)
得到与sum(B)
相同的结果,但sum(A, dims=1)
是一个包含每一列总和的行向量,sum(A, dims=2)
是一个包含每一行总和的列向量。这与 R 的行为形成了对比,在 R 中,单独的colSums(B)
和rowSums(B)
提供了这些功能。如果dims
关键字参数是向量,则它指定执行求和的所有维度,并同时保持待求和数组的维数,例如sum(A, dims=(1,2)) == hcat(10)
。应该注意的是,没有针对第二个参数的错误检查。
在 R 中,高性能需要向量化。在 Julia 中,这几乎恰恰相反:性能最高的代码通常通过去向量化的循环来实现。
Julia 是立即求值的,不支持 R 风格的惰性求值。对于大多数用户来说,这意味着很少有未引用的表达式或列名。
Julia 不支持
NULL
类型。最接近的等价物是nothing
,但它的行为类似于标量值而不是列表。请使用x === nothing
代替is.null(x)
。在 Julia 中,缺失值由
missing
表示,而不是由NA
表示。请使用ismissing(x)
(或者在向量上使用逐元素操作ismissing.(x)
)代替isna(x)
。通常使用skipmissing
代替na.rm=TRUE
(尽管在某些特定情况下函数接受skipmissing
参数)。
- Julia 缺少 R 中的
assign
或get
的等价物。 - 在 Julia 中,
return
不需要括号。 - 在 R 中,删除不需要的值的惯用方法是使用逻辑索引,如表达式
x[x>3]
或语句x = x[x>3]
来 in-place 修改x
。相比之下,Julia 提供了更高阶的函数filter
和filter!
,允许用户编写filter(z->z>3, x)
和filter!(z->z>3, x)
来代替相应直译x[x.>3]
和x = x[x.>3]
。使用filter!
可以减少临时数组的使用。
与 Python 的显著差异
Julia 的
for
,if
,while
等语句块都以end
关键字结束。代码的缩进不像在 Python 中那样重要。Julia 也没有pass
关键字。Julia 中的字符串使用双引号构造,如
"text"
,也可以使用三引号构造多行字符串。而在 Python 中可以使用单引号('text'
)或者双引号("text"
)。单引号在 Julia 中用来表示单个字符,例如'c'
。- 在 Julia 中字符串的拼接使用
*
,而不是像 Python 一样使用+
。类似的,字符串重复多次 Julia 使用^
而不是*
。Julia 也不支持隐式的字符串拼接,例如 Python 中的'ab' 'cd' == 'abcd'
。 - Python 列表——灵活但缓慢——对应于 Julia 的
Vector{Any}
类型或更一般的Vector{T}
,其中T
是一些非具体元素类型。 “快”的数组,如 NumPy 数组,它们就地存储元素(即,dtype
是np.float64
、[('f1', np.uint64), ('f2', np.int32)]
, 等)可以用Array{T}
表示,其中T
是一个具体的、不可变的元素类型。 这包括内置类型,如Float64
、Int32
、Int64
,也包括更复杂的类型,如Tuple{UInt64,Float64}
和许多用户定义的类型。 - 在 Julia 中,数组、字符串等的索引从 1 开始,而不是从 0 开始。
Julia 里的切片包含最后一个元素。Julia 里的
a[2:3]
等同于 Python 中的a[1:3]
。Julia 不支持负数索引。特别地,列表或数组的最后一个元素在 Julia 中使用
end
索引,而不像在 Python 中使用-1
。Julia 的索引必须写全。Python 中的
x[1:]
等价于 Julia 中的x[2:end]
。- Julia 的范围语法为
x[start:step:stop]
,而 Python 的格式为x[start:(stop+1):step]
。 因此 Python 中的x[0:10:2]
等价于 Julia 里的x[1:2:10]
。类似的 Python 中的反转数组x[::-1]
等价于 Julia 中的x[end:-1:1]
。 - 在 Julia 中队一个矩阵取索引
X[[1,2], [1,3]]
返回一个子矩阵,它包含了第一和第二行与第一和第三列的交集。 在 Python 中X[[1,2], [1,3]]
返回一个向量,它包含索引[1,1]
和[2,3]
的值。Julia 中的X[[1,2], [1,3]]
等价于 Python 中的X[np.ix_([0,1],[0,2])]
。Python 中的X[[1,2], [1,3]]
等价于 Julia 中的X[[CartesianIndex(1,1), CartesianIndex(2,3)]]
。 - Julia 没有用来续行的语法:如果在行的末尾,到目前为止的输入是一个完整的表达式,则认为其已经结束;否则,认为输入继续。强制表达式继续的一种方式是将其包含在括号中。
- 默认情况下,Julia 数组是列优先(Fortran 排序),而 NumPy 数组是行优先(C 排序)。为了在循环数组时获得最佳性能,Julia 中的循环顺序应相对于 NumPy 颠倒(请参阅性能提示的相关部分)。
- Julia 的更新运算符(例如
+=
,-=
,···)是非原位操作(not in-place),而 Numpy 的是。这意味着A = [1, 1]; B = A; B += [3, 3]
不会改变A
中的值,而将名称B
重新绑定到右侧表达式B = B + 3
的结果,这是一个新的数组。对于 in-place 操作,使用B .+= 3
(另请参阅 dot operators)、显式的循环或者InplaceOps.jl
。
- Julia 的函数在调用时,每次都对默认参数重新求值,不像 Python 只在函数定义时对默认参数求一次值。
举例来说:Julia 的函数
f(x=rand()) = x
在无参数调用时(f()
),每次都会返回不同的随机数。 另一方面,函数g(x=[1,2]) = push!(x,3)
无参数调用时g()
,永远返回[1,2,3]
。
- 在 Julia 中,必须使用关键字来传递关键字参数,这与 Python 中通常可以按位置传递它们不同。尝试按位置传递关键字参数会改变方法签名,从而导致
MethodError
或调用错误的方法。
- 在 Julia 中,
%
是余数运算符,而在 Python 中是模运算符。 (译注:二者在参数有负数时有区别) - 在 Julia 中,常用的整数类型 Int 对应机器的整数类型,
Int32
或Int64
。不像 Python 中的整数 int 是任意精度的。这意味着 Julia 中默认的整数类型会溢出,因此2^64 == 0
。如果你要表示一个大数,请选择一个合适的类型。如:Int128
、任意精度的BigInt
或者浮点类型Flost64
。
- Julia 中虚数单位
sqrt(-1)
是im
,而不是 Python 中的j
。 - Julia 中指数是
^
,而不是 Python 中的**
。 - Julia 使用
Nothing
类型的实例nothing
代表空值(null),而不是 Python 中NoneType
类的None
。 - 在 Julia 中,标准的运算符作用在矩阵上就得到矩阵操作,不像 Python 标准运算符默认是逐元素操作。当 A 和 B 都是矩阵时,
A * B
在 Julia 中代表着矩阵乘法,而不是 Python 中的逐元素相乘。即:Julia 中的A * B
等同于 Python 的A @ B
;Python 中的A * B
等同于 Julia 中的A .* B
。 - Julia 中的伴随操作符
'
返回向量的转置(一种行向量的懒惰表示法)。Python 中对向量执行.T
返回它本身(没有效果)。 - 在Julia中,一个函数可能包含多个具体实现(称为方法),与Python中的函数相比,这些实现是根据调用的所有参数的类型通过多重派发选择的,它只有一个实现,没有多态性(与Python方法调用相反,Python方法调用使用不同的语法,并允许在方法的接收者上进行派发)。
- Julia 没有类(class),取而代替的是结构体(structures),可以是可变的或不可变的,它们只包含数据而不包含方法。
- 在 Python 中调用类实例的方法 (
x = MyClass(*args); x.f(y)
) 对应于 Julia 中的函数调用,例如x = MyType(args...); f(x, y)
。 总的来说,多重派发比 Python 类系统更灵活和强大。 - Julia 的结构体有且只能有一个抽象超类型(abstract supertype),而 Python 的类可以纪成一个或多个、抽象或具体的超类(superclasses)。
- 逻辑 Julia 程序结构(包和模块)独立于文件结构(
include
用于附加文件),而 Python 代码结构由目录(包)和文件(模块)定义。 - Julia 中的三元运算符
x > 0 ? 1 : -1
对应于 Python 中的条件表达式1 if x > 0 else -1
。 - Julia 中以
@
开头的符号是宏(macro),而 Python 中是装饰器(decorator)。 - Julia 的异常处理使用
try
—catch
—finally
,而不是 Python 的try
—except
—finally
。与 Python 不同的是,因为性能的原因,Julia 不推荐在正常流程中使用异常处理。 - Julia 的循环很快,所以没必要手动向量化(vectorized)。
- 小心 Julia 中的非常量全局变量,尤其它出现在循环中时。因为你在 Julia 中可以写出贴近硬件的代码,这时使用全局变量的影响非常大(参见性能建议)
- Python 中大多数的值都能用在逻辑运算中。例如:
if "a"
永真,if ""
恒假。在 Julia 中你只能使用布尔类型的值,或者显示的将其他值转为布尔类型,否则就会抛出异常。例如当你想测试字符串是否为空是,请使用if !isempty("")
。 - 在 Julia 中大多数代码块都会引入新的本地作用域(local scope)。例如:循环和异常处理的 try — catch — finally。注意:列表推断(comprehensions)与生成器在 Julia 和 Python 中都会引入新的作用域;而 if 分支则都不会引入。
与 C/C++ 的显著差异
- Julia 的数组由方括号索引,方括号中可以包含不止一个维度
A[i,j]
。这样的语法不仅仅是像 C/C++ 中那样对指针或者地址引用的语法糖,参见关于数组构造的语法的 Julia 文档。
- 在 Julia 中,数组、字符串等的索引从 1 开始,而不是从 0 开始。
- Julia 的数组在赋值给另一个变量时不发生复制。执行
A = B
后,改变B
中元素也会修改A
。像+=
这样的更新运算符不会以 in-place 的方式执行,而是相当于A = A + B
,将左侧绑定到右侧表达式的计算结果上。
- Julia 的数组是列优先的(Fortran 顺序),而 C/C++ 的数组默认是行优先的。要使数组上的循环性能最优,在 Julia 中循环的顺序应该与 C/C++ 相反(参见 性能建议)。
Julia 的值在赋值或向函数传递时不发生复制。如果某个函数修改了数组,这一修改对调用者是可见的。
在 Julia 中,空格是有意义的,这与 C/C++ 不同,所以向 Julia 程序中添加或删除空格时必须谨慎。
在 Julia 中,没有小数点的数值字面量(如
42
)生成有符号整数,类型为Int
,但如果字面量太长,超过了机器字长,则会被自动提升为容量更大的类型,例如Int64
(如果Int
是Int32
)、Int128
,或者任意精度的BigInt
类型。不存在诸如L
,LL
,U
,UL
,ULL
这样的数值字面量后缀指示无符号和/或有符号与无符号。十进制字面量始终是有符号的,十六进制字面量(像 C/C++ 一样由0x
开头)是无符号的。另外,十六进制字面量与 C/C++/Java 不同,也与 Julia 中的十进制字面量不同,它们的类型取决于字面量的长度,包括开头的 0。例如,0x0
和0x00
的类型是UInt8
,0x000
和0x0000
的类型是UInt16
。同理,字面量的长度在 5-8 之间,类型为UInt32
;在 9-16 之间,类型为UInt64
;在 17-32 之间,类型为UInt128
。当定义十六进制掩码时,就需要将这一问题考虑在内,比如~0xf == 0xf0
与~0x000f == 0xfff0
完全不同。64 位Float64
和 32 位Float32
的字面量分别表示为1.0
和1.0f0
。浮点字面量在无法被精确表示时舍入(且不会提升为BigFloat
类型)。浮点字面量在行为上与 C/C++ 更接近。八进制(前缀为0o
)和二进制(前缀为0b
)也被视为无符号的。
在 Julia 中,当两个操作数都是整数类型时,除法运算符
/
返回一个浮点数。 要执行整数除法,请使用div
或÷
。使用浮点类型索引数组在 Julia 中通常是错误的。 C 表达式
a[i / 2]
的 Julia 等价写法是a[i ÷ 2 + 1]
,其中i
是整数类型。字符串字面量可用
"
或"""
分隔,用"""
分隔的字面量可以包含"
字符而无需像"\""
这样来引用它。字符串字面量可以包含插入其中的其他变量或表达式,由$variablename
或$(expression)
表示,它在该函数所处的上下文中计算变量名或表达式。
//
表示Rational
数,而非单行注释(其在 Julia 中是#
)#=
表示多行注释的开头,=#
结束之。- Julia 中的函数返回其最后一个表达式或
return
关键字的值。可以从函数中返回多个值并将其作为元组赋值,如(a, b) = myfunction()
或a, b = myfunction()
,而不必像在 C/C++ 中那样必须传递指向值的指针(即a = myfunction(&b)
)。
- Julia 不要求使用分号来结束语句。表达式的结果不会自动打印(除了在交互式提示符中,即 REPL),且代码行不需要以分号结尾。
println
或@printf
可用于打印特定输出。在 REPL 中,;
可用于抑制输出。;
在[ ]
中也有不同的含义,需要注意。;
可用于在单行中分隔表达式,但在许多情况下不是绝对必要的,更经常是为了可读性。
Julia 有两个右移运算符,
>>
和>>>
。>>
执行算术移位,>>>
始终执行逻辑移位,这与 C/C++ 不同,其中>>
的含义取决于被移位的值的类型。
- Julia 的
->
创建一个匿名函数,它并不通过指针访问成员。 Julia 在编写
if
语句或for
/while
循环时不需要括号:请使用for i in [1, 2, 3]
代替for (int i=1; i <= 3; i++)
,以及if i == 1
代替if (i == 1)
Julia 不把数字
0
和1
视为布尔值。在 Julia 中不能编写if (1)
,因为if
语句只接受布尔值。相反,可以编写if true
、if Bool(1)
或if 1==1
。
- Julia 使用
end
来表示条件块(如if
)、循环块(如while
/for
)和函数的结束。为了代替单行if ( cond ) statement
,Julia 允许形式为if cond; statement; end
、cond && statement
和!cond || statement
的语句。后两种语法中的赋值语句必须显式地包含在括号中,例如cond && (x = value)
,这是因为运算符的优先级。
- Julia 没有用来续行的语法:如果在行的末尾,到目前为止的输入是一个完整的表达式,则认为其已经结束;否则,认为输入继续。强制表达式继续的一种方式是将其包含在括号中。
- Julia 宏对已解析的表达式进行操作,而非程序的文本,这允许它们执行复杂的 Julia 代码转换。宏名称以
@
字符开头,具有类似函数的语法@mymacro(arg1, arg2, arg3)
和类似语句的语法@mymacro arg1 arg2 arg3
。两种形式的语法可以相互转换;如果宏出现在另一个表达式中,则类似函数的形式尤其有用,并且它通常是最清晰的。类似语句的形式通常用于标注块,如在分布式for
结构中:@distributed for i in 1:n; #= body =#; end
。如果宏结构的结尾不那么清晰,请使用类似函数的形式。
Julia 有一个枚举类型,使用宏
@enum(name, value1, value2, ...)
来表示,例如:@enum(Fruit, banana=1, apple, pear)
。按照惯例,修改其参数的函数在名称的末尾有个
!
,例如push!
。在 C++ 中,默认情况下,你具有静态分派,即为了支持动态派发,你需要将函数标注为 virtual 函数。另一方面,Julia 中的每个方法都是「virtual」(尽管它更通用,因为方法是在每个参数类型上派发的,而不仅仅是
this
,并且使用的是最具体的声明规则)。
与 Common Lisp 的显著差异
Julia 默认使用 1 开始的数组索引,它也能处理任意的索引顺序。
函数和变量共用一个命名空间(”Lisp-1”)。
Julia 中有
Pair
类型,但这并不意味着它能用作COMMON-LISP:CONS
。在 Julia 中绝大多数可迭代的集合都能互换使用,例如:集合解包(splatting)、元组等。Tuple
最接近 Common Lisp 中用于异构元素的短列表。使用NamedTuple
来代替关联表(alists)。对于更大的、同类型元素的集合,应该使用Array
和Dict
。典型的使用 Julia 进行原型开发时,也会对镜像进行连续的修改,Revise.jl 包提供了这个功能。
对于性能,Julia 更喜欢操作具有 类型稳定性。 Common Lisp 从底层机器操作中抽象出来,而 Julia 则更接近它们。 例如:
模块(名称空间)可以是分层的。
import
和using
有着双重角色:他们加载代码并让代码在命名空间中可用。import
用于仅有模块名是可用的情况,大致等价于ASDF:LOAD-OP
。槽名(Slot name)不需要单独导出。全局变量不能从模块的外部赋值,除了eval(mod, :(var = val))
这个例外情况。宏以
@
开头,并没有像 Common Lisp 那样无缝地集成到语言中;因此在 Julia 中,宏的使用不像在 Common Lisp 中那样广泛。Julia 支持宏的一种卫生(hygiene)形式。因为不同的表层语法,Julia 中没有COMMON-LISP:&BODY
的等价形式。所有的函数都是通用的并且使用多重分派。函数的参数列表也无需遵循一样的模板,这让我们有了一个强大的范式:
do
。可选参数与关键字参数的处理方式不同。方法的歧义没有像在 Common Lisp 对象系统中那样得到解决,因此需要为交集定义更具体的方法。符号不属于任何包,它本身也不包含任何值。
M.var
会对M
模块里的var
符号求值。Julia 完全支持函数式编程风格,包括闭包等特性。但这并不是 Julia 的惯用风格。修改捕获变量时需要一些额外的变通以便提高性能。