与其他语言的显著差异
与 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 的惯用风格。修改捕获变量时需要一些额外的变通以便提高性能。
