常见问题
会话和 REPL
如何从内存中删除某个对象?
Julia 没有类似于 MATLAB 的 clear
函数,某个名称一旦定义在 Julia 的会话中(准确地说,在 Main
模块中),它就会一直存在下去。
如果关心内存用量,一个对象总能被一个占用更少内存的对象替换掉。例如,如果 A
是一个不再需要的 GB 量级的数组,可以使用 A = nothing
来释放内存。该内存将在下一次垃圾回收器运行时被释放,也可以使用 [gc()
]
该如何检查当前文件是否正在以主脚本运行?
当一个文件通过使用julia file.jl
来当做主脚本运行时,有人也希望激活另外的功能例如命令行参数操作。确定文件是以这个方式运行的一个方法是检查abspath(PROGRAM_FILE) == @__FILE__
是不是true
。
How do I catch CTRL-C in a script?
Running a Julia script using julia file.jl
does not throw InterruptException
when you try to terminate it with CTRL-C (SIGINT). To run a certain code before terminating a Julia script, which may or may not be caused by CTRL-C, use atexit
. Alternatively, you can use julia -e 'include(popfirst!(ARGS))' file.jl
to execute a script while being able to catch InterruptException
in the try
block.
How do I pass options to julia
using #!/usr/bin/env
?
Passing options to julia
in so-called shebang by, e.g., #!/usr/bin/env julia --startup-file=no
may not work in some platforms such as Linux. This is because argument parsing in shebang is platform-dependent and not well-specified. In a Unix-like environment, a reliable way to pass options to julia
in an executable script would be to start the script as a bash
script and use exec
to replace the process to julia
:
#!/bin/bash
#=
exec julia --color=yes --startup-file=no -e 'include(popfirst!(ARGS))' \
"${BASH_SOURCE[0]}" "$@"
=#
@show ARGS # put any Julia code here
In the example above, the code between #=
and =#
is run as a bash
script. Julia ignores this part since it is a multi-line comment for Julia. The Julia code after =#
is ignored by bash
since it stops parsing the file once it reaches to the exec
statement.
函数
向函数传递了参数 x
,在函数中做了修改,但是在函数外变量 x
的值还是没有变。为什么?
假设函数被如此调用:
julia> x = 10
10
julia> function change_value!(y)
y = 17
end
change_value! (generic function with 1 method)
julia> change_value!(x)
17
julia> x # x is unchanged!
10
在 Julia 中,通过将 x
作为参数传递给函数,不能改变变量 x
的绑定。在上例中,调用 change_value!(x)
时,y
是一个新建变量,初始时与 x
的值绑定,即 10
。然后 y
与常量 17
重新绑定,此时变量外作用域中的 x
并没有变动。
但是这里有一个需要注意的点:假设 x
被绑定至 Array
类型 (或者其他 可变 的类型)。在函数中,你无法将 x
与 Array “解绑”,但是你可以改变其内容。例如:
julia> x = [1,2,3]
3-element Array{Int64,1}:
1
2
3
julia> function change_array!(A)
A[1] = 5
end
change_array! (generic function with 1 method)
julia> change_array!(x)
5
julia> x
3-element Array{Int64,1}:
5
2
3
这里我们新建了一个函数 chang_array!
,它把 5
赋值给传入的数组(在调用处与 x
绑定,在函数中与 A
绑定)的第一个元素。注意,在函数调用之后,x
依旧与同一个数组绑定,但是数组的内容变化了:变量 A
和 x
是不同的绑定,引用同一个可变的 Array
对象。
函数内部能否使用 using
或 import
?
不可以,不能在函数内部使用 using
或 import
语句。如果你希望导入一个模块,但只在特定的一个或一组函数中使用它的符号,有以下两种方式:
使用
import
:import Foo function bar(...) # ... refer to Foo symbols via Foo.baz ... end
这会加载
Foo
模块,同时定义一个变量Foo
引用该模块,但并不会 将其他任何符号从该模块中导入当前的命名空间。Foo
等符号可以由限定的名称Foo.bar
等引用。将函数封装到模块中:
module Bar export bar using Foo function bar(...) # ... refer to Foo.baz as simply baz .... end end using Bar
这会从
Foo
中导入所有符号,但仅限于Bar
模块内。
运算符 ...
有何作用?
...
运算符的两个用法:slurping 和 splatting
很多 Julia 的新手会对运算符 ...
的用法感到困惑。让 ...
用法如此困惑的部分原因是根据上下文它有两种不同的含义。
...
在函数定义中将多个参数组合成一个参数
在函数定义的上下文中,...
运算符用来将多个不同的参数组合成单个参数。...
运算符的这种将多个不同参数组合成单个参数的用法称为slurping:
julia> function printargs(args...)
println(typeof(args))
for (i, arg) in enumerate(args)
println("Arg #$i = $arg")
end
end
printargs (generic function with 1 method)
julia> printargs(1, 2, 3)
Tuple{Int64,Int64,Int64}
Arg #1 = 1
Arg #2 = 2
Arg #3 = 3
如果Julia是一个使用ASCII字符更加自由的语言的话,slurping运算符可能会写作<-...
而非...
。
...
在函数调用中将一个参数分解成多个不同参数
与在定义函数时表示将多个不同参数组合成一个参数的...
运算符用法相对,当用在函数调用的上下文中...
运算符也用来将单个的函数参数分成多个不同的参数。...
函数的这个用法叫做splatting:
julia> function threeargs(a, b, c)
println("a = $a::$(typeof(a))")
println("b = $b::$(typeof(b))")
println("c = $c::$(typeof(c))")
end
threeargs (generic function with 1 method)
julia> x = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> threeargs(x...)
a = 1::Int64
b = 2::Int64
c = 3::Int64
如果Julia是一个使用ASCII字符更加自由的语言的话,splatting运算符可能会写作...->
而非...
。
赋值语句的返回值是什么?
=
运算符始终返回右侧的值,所以:
julia> function threeint()
x::Int = 3.0
x # returns variable x
end
threeint (generic function with 1 method)
julia> function threefloat()
x::Int = 3.0 # returns 3.0
end
threefloat (generic function with 1 method)
julia> threeint()
3
julia> threefloat()
3.0
相似地:
julia> function threetup()
x, y = [3, 3]
x, y # returns a tuple
end
threetup (generic function with 1 method)
julia> function threearr()
x, y = [3, 3] # returns an array
end
threearr (generic function with 1 method)
julia> threetup()
(3, 3)
julia> threearr()
2-element Array{Int64,1}:
3
3
类型,类型声明和构造函数
[何谓“类型稳定”?]为什么对于同一个流的并发写入会导致相互混合的输出?
虽然流式 I/O 的 API 是同步的,底层的实现是完全异步的。
思考一下下面的输出:
julia> @sync for i in 1:3
@async write(stdout, string(i), " Foo ", " Bar ")
end
123 Foo Foo Foo Bar Bar Bar
这是因为,虽然write
调用是同步的,每个参数的写入在等待那一部分I/O完成时会生成其他的Tasks。
print
和println
在调用中会"锁定"该流。因此把上例中的write
改成println
会导致:
julia> @sync for i in 1:3
@async println(stdout, string(i), " Foo ", " Bar ")
end
1 Foo Bar
2 Foo Bar
3 Foo Bar
你可以使用ReentrantLock
来锁定你的写入,就像这样:
julia> l = ReentrantLock()
ReentrantLock(nothing, Condition(Any[]), 0)
julia> @sync for i in 1:3
@async begin
lock(l)
try
write(stdout, string(i), " Foo ", " Bar ")
finally
unlock(l)
end
end
end
1 Foo Bar 2 Foo Bar 3 Foo Bar
数组
零维数组和标量之间的有什么差别?
零维数组是Array{T,0}
形式的数组,它与标量的行为相似,但是有很多重要的不同。这值得一提,因为这是使用数组的范用定义来解释也符合逻辑的特殊情况,虽然最开始看起来有些非直觉。下面一行定义了一个零维数组:
julia> A = zeros()
0-dimensional Array{Float64,0}:
0.0
在这个例子中,A
是一个含有一个元素的可变容器,这个元素可以通过A[] = 1.0
来设置,通过A[]
来读取。所有的零维数组都有同样的大小(size(A) == ()
)和长度(length(A) == 1
)。特别地,零维数组不是空数组。如果你觉得这个非直觉,这里有些想法可以帮助理解Julia的这个定义。
- 类比的话,零维数组是"点",向量是"线"而矩阵 是"面"。就像线没有面积一样(但是也能代表事物的一个集合), 点没有长度和任意一个维度(但是也能表示一个事物)。
- 我们定义
prod(())
为1,一个数组中的所有的元素个数是 大小的乘积。零维数组的大小为()
,所以 它的长度为1
。 - 零维数组原生没有任何你可以索引的维度 -- 它们仅仅是
A[]
。我们可以给它们应用同样的"trailing one"规则, 如同所有其他的数组维度一样,所以你实际上可以使用A[1]
,A[1,1]
等来索引
理解它与普通的标量之间的区别也很重要。标量不是一个可变的容器(尽管它们是可迭代的,可以定义像length
,getindex
这样的东西,例如1[] == 1
)。特别地,如果x = 0.0
是以一个标量来定义,尝试通过x[] = 1.0
来改变它的值会报错。标量x
能够通过fill(x)
转化成包含它的零维数组,并且相对地,一个零维数组a
可以通过a[]
转化成其包含的标量。另外一个区别是标量可以参与到线性代数运算中,比如2 * rand(2,2)
,但是零维数组的相似操作fill(2) * rand(2,2)
会报错。
Julia 版本发布
应该使用 Julia 的正式版(release version),测试版(beta version)还是每夜更新版(nightly version)?
如果您正在寻找稳定的代码库,您可能更喜欢Julia的正式版。 通常每6个月发布一次,为您提供编写代码的稳定平台。
如果您不介意稍微落后于最新的错误修正和更改,觉得稍微更快的修改更具吸引力,您可能更喜欢Julia的测试版。 此外,这些二进制文件在发布之前会进行测试,以确保它们完全正常运行。
如果您想利用该语言的最新更新,并且不介意今天可用的版本偶尔出现实际上并没有正常工作,您可能更喜欢 Julia 的每夜更新版。
最后,您也可以考虑自己从源代码编译Julia。 此选项主要适用于那些适应命令行或对学习感兴趣的人。 如果您是这样,您可能也有兴趣阅读我们的贡献指南。
可以在https://julialang.org/downloads/的下载页面上找到每种下载类型的链接。 请注意,并非所有版本的Julia都适用于所有平台。
已弃用的功能会在何时移除?
已弃用的函数会被随后发布的版本中移除。例如,在1.0版本中被标记为已弃用的函数会在0.2版本及之后的版本中无法使用。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论