返回介绍

常见问题

发布于 2019-07-03 15:53:54 字数 9738 浏览 1155 评论 0 收藏 0

会话和 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 依旧与同一个数组绑定,但是数组的内容变化了:变量 Ax 是不同的绑定,引用同一个可变的 Array 对象。

函数内部能否使用 usingimport

不可以,不能在函数内部使用 usingimport 语句。如果你希望导入一个模块,但只在特定的一个或一组函数中使用它的符号,有以下两种方式:

  1. 使用 import

    import Foo
    function bar(...)
        # ... refer to Foo symbols via Foo.baz ...
    end

    这会加载 Foo 模块,同时定义一个变量 Foo 引用该模块,但并不会 将其他任何符号从该模块中导入当前的命名空间。 Foo 等符号可以由限定的名称 Foo.bar 等引用。

  2. 将函数封装到模块中:

    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。

printprintln在调用中会"锁定"该流。因此把上例中的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]等来索引

理解它与普通的标量之间的区别也很重要。标量不是一个可变的容器(尽管它们是可迭代的,可以定义像lengthgetindex这样的东西,例如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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文