diff --git a/Project.toml b/Project.toml index c0f8e3f5..2ba4df8f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "JuliaZH" uuid = "652e05fd-ed22-5b6c-bf99-44e63a676e5f" -version = "1.1.0" +version = "1.3.1" [deps] REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" diff --git a/doc/src/index.md b/doc/src/index.md index bd47f845..368ff6bb 100644 --- a/doc/src/index.md +++ b/doc/src/index.md @@ -1,6 +1,6 @@ -# Julia 1.1 中文文档 +# Julia 1.3 中文文档 -欢迎来到 Julia 1.1 中文文档([PDF版本](https://raw.githubusercontent.com/JuliaCN/JuliaZH.jl/pdf/dev/Julia中文文档.pdf))! +欢迎来到 Julia 1.3 中文文档([PDF版本](https://raw.githubusercontent.com/JuliaCN/JuliaZH.jl/pdf/v1.3.1/Julia中文文档-1.3.1.pdf))! 请先阅读 [v1.0 正式发布博文](https://julialang.org/blog/2018/08/one-point-zero-zh_cn) 以获得对这门语言的总体概观。我们推荐刚刚开始学习 Julia 语言的朋友阅读中文社区提供的 [Julia入门指引](https://discourse.juliacn.com/t/topic/159),也推荐你使用[discourse](https://discourse.juliacn.com)对遇到的问题进行提问。 diff --git a/zh_CN/doc/src/base/multi-threading.md b/zh_CN/doc/src/base/multi-threading.md index add2e0fd..9892d853 100644 --- a/zh_CN/doc/src/base/multi-threading.md +++ b/zh_CN/doc/src/base/multi-threading.md @@ -28,7 +28,7 @@ Base.Threads.atomic_fence Base.@threadcall ``` -## 同步原始类型 +## 同步基元 ```@docs Base.Threads.AbstractLock diff --git a/zh_CN/doc/src/devdocs/ast.md b/zh_CN/doc/src/devdocs/ast.md index 4a206849..dc21c4d2 100644 --- a/zh_CN/doc/src/devdocs/ast.md +++ b/zh_CN/doc/src/devdocs/ast.md @@ -13,7 +13,7 @@ Julia 有两种代码的表现形式。 * `Expr` Has a node type indicated by the `head` field, and an `args` field which is a `Vector{Any}` of - subexpressions. + 子表达式 尽管表层 AST 中,几乎每个部分都是通过 `Expr` 表示的,中间表现形式只四用了很有限的 `Expr`,主要用于调动、条件分支 (`gotoifnot`) 和返回。 limited number of `Expr`s, mostly for calls, conditional branches (`gotoifnot`), and returns. diff --git a/zh_CN/doc/src/devdocs/valgrind.md b/zh_CN/doc/src/devdocs/valgrind.md index 5b257621..fed89d9d 100644 --- a/zh_CN/doc/src/devdocs/valgrind.md +++ b/zh_CN/doc/src/devdocs/valgrind.md @@ -1,4 +1,4 @@ -# Using Valgrind with Julia +# 在Julia中使用Valgrind [Valgrind](http://valgrind.org/) is a tool for memory debugging, memory leak detection, and profiling. This section describes things to keep in mind when using Valgrind to debug memory issues with diff --git a/zh_CN/doc/src/manual/arrays.md b/zh_CN/doc/src/manual/arrays.md index a2d7a37a..81708dc0 100644 --- a/zh_CN/doc/src/manual/arrays.md +++ b/zh_CN/doc/src/manual/arrays.md @@ -753,7 +753,11 @@ julia> broadcast(+, a, b) 类似 `.+` 和 `.*` 的[点运算符](@ref man-dot-operators) 等同于 `broadcast` 调用(除了它们融合两种操作,如下所述)。还有一个 [`broadcast!`](@ref) 函数来指定一个显式目标(也可以通过 `.=` 赋值以融合的方式访问它)。事实上,`f.(args...)` 等价于 `broadcast(f, args...)`,并为广播任何函数提供了方便的语法([dot syntax](@ref man-vectorized))。嵌套的「点调用」`f.(...)`(包括调用 `.+` 等)[自动融合](@ref man-dot-operators)到单个 `broadcast` 调用。 -另外,[`broadcast`](@ref) 并不局限于数组(参见函数文档),对于元组依然有效。对于其他非数组,元组或引用 [`Ref`](@ref)(除了指针 [`Ptr`](@ref)) 的参数,视作“标量”。 +Additionally, [`broadcast`](@ref) is not limited to arrays (see the function documentation); +it also handles scalars, tuples and other collections. By default, only some argument types are +considered scalars, including (but not limited to) `Number`s, `String`s, `Symbol`s, `Type`s, `Function`s +and some common singletons like `missing` and `nothing`. All other arguments are +iterated over or indexed into elementwise. ```jldoctest julia> convert.(Float32, [1, 2]) diff --git a/zh_CN/doc/src/manual/calling-c-and-fortran-code.md b/zh_CN/doc/src/manual/calling-c-and-fortran-code.md index a48dbf6c..58234412 100644 --- a/zh_CN/doc/src/manual/calling-c-and-fortran-code.md +++ b/zh_CN/doc/src/manual/calling-c-and-fortran-code.md @@ -3,42 +3,54 @@ 在数值计算领域,尽管有很多用 C 语言或 Fortran 写的高质量且成熟的库都可以用 Julia 重写,但为了便捷利用现有的 C 或 Fortran 代码,Julia 提供简洁且高效的调用方式。Julia 的哲学是 `no boilerplate`: Julia 可以直接调用 C/Fortran 的函数,不需要任何"胶水"代码,代码生成或其它编译过程 -- 即使在交互式会话 (REPL/Jupyter notebook) 中使用也一样. 在 Julia 中,上述特性可以仅仅通过调用 [`ccall`](@ref) 实现,它的语法看起来就像是普通的函数调用。 -被调用的代码必须是一个共享库(.so, .dylib, .dll)。大多数 C 和 Fortran 库都已经是以共享库的形式发布的,但在用 GCC 或 Clang 编译自己的代码时,需要添加 `-shared` 和 `-fPIC` 编译器选项。由于 Julia 的 JIT 生成的机器码跟原生 C 代码的调用是一样,所以在 Julia 里调用 C/Fortran 库的额外开销与直接从 C 里调用是一样的。在 C 和 Julia 中的非库函数调用都能被内联,因此可能会比调用标准库函数开销更少。当库与可执行文件都由 LLVM 生成时,对程序整体进行优化从而跨越这一限制是可能的,但是 Julia 现在还不支持这种优化。不过在未来,Julia 可能会支持,从而获得更大的性能提升。 +The code to be called must be available as a shared library. Most C and Fortran libraries ship +compiled as shared libraries already, but if you are compiling the code yourself using GCC (or +Clang), you will need to use the `-shared` and `-fPIC` options. The machine instructions generated +by Julia's JIT are the same as a native C call would be, so the resulting overhead is the same +as calling a library function from C code. [^1] -我们可以通过 `(:function, "library")` 或 `("function", "library")` 这两种形式来索引库中的函数,其中 `function` 是函数名,`library` 是库名。对于不同的平台/操作系统,库的载入路径可能会不同,如果库在默认载入路径中,则可以直接将 `library` 设为库名,否则,需要将其设为一个完整的路径。 +Shared libraries and functions are referenced by a tuple of the form `(:function, "library")` +or `("function", "library")` where `function` is the C-exported function name, and `library` refers +to the shared library name. Shared libraries available in the (platform-specific) load path will +be resolved by name. The full path to the library may also be specified. 可以单独使用函数名来代替元组(只用 `:function` 或 `"function"`)。在这种情况下,函数名在当前进程中进行解析。这一调用形式可用于调用 C 库函数、Julia 运行时中的函数或链接到 Julia 的应用程序中的函数。 默认情况下,Fortran 编译器会[进行名称修饰](https://en.wikipedia.org/wiki/Name_mangling#Fortran)(例如,将函数名转换为小写或大写,通常会添加下划线),要通过 [`ccall`](@ref) 调用 Fortran 函数,传递的标识符必须与 Fortran 编译器名称修饰之后的一致。此外,在调用 Fortran 函数时,**所有**输入必须以指针形式传递,并已在堆或栈上分配内存。这不仅适用于通常是堆分配的数组及可变对象,而且适用于整数和浮点数等标量值,尽管这些值通常是栈分配的,且在使用 C 或 Julia 调用约定时通常是通过寄存器传递的。 -最终,你能使用 [`ccall`](@ref) 来实际生成一个对库函数的调用。[`ccall`](@ref) 的参数如下: +Finally, you can use [`ccall`](@ref) to actually generate a call to the library function. The arguments +to [`ccall`](@ref) are: -1. 一个 `(:function, "library")` 元组,必须为常数字面量的形式, +1. A `(:function, "library")` pair (most common), 或 - a `:function` name symbol or `"function"` name string, which is resolved in the - 当前进程, + a `:function` name symbol or `"function"` name string (for symbols in the current process or libc), 或 一个函数指针(例如,从 `dlsym` 获得的指针)。 -2. 返回类型(参见下文,将声明的 C 类型对应到 Julia) +2. The function's return type - * 当包含的函数已经定义时,参数将会在编译期执行。 +3. A tuple of input types, corresponding to the function signature -3. 输入类型的元组。元组中的类型必须为字面量,而不能是变量或者表达式。 - +4. The actual argument values to be passed to the function, if any; each is a separate parameter. - * 当包含的函数已经定义时,参数将会在编译期执行。 +!!! note + The `(:function, "library")` pair, return type, and input types must be literal constants + (i.e., they can't be variables, but see [Non-constant Function Specifications](@ref) below). + + The remaining parameters are evaluated at compile time, when the containing method is defined. -4. 紧接着的参数,如果有的话,将会以参数的实际值传递给函数。 +!!! note + See below for how to [map C types to Julia types](@ref mapping-c-types-to-julia). -举一个完整而简单的例子:从 C 的标准库函数中调用 `clock` 函数。 +As a complete but simple example, the following calls the `clock` function from the standard C +library on most Unix-derived systems: ```julia-repl -julia> t = ccall((:clock, "libc"), Int32, ()) +julia> t = ccall(:clock, Int32, ()) 2292761 julia> t @@ -48,10 +60,12 @@ julia> typeof(ans) Int32 ``` -`clock` 不接收任何参数,它会返回一个类型为 [`Int32`](@ref) 的值。一个常见的问题是必须要用尾随的逗号来写一个单元组。例如,要通过 `getenv` 函数来获取一个指向环境变量值的指针,可以像这样调用: +`clock` takes no arguments and returns an [`Int32`](@ref). One common gotcha is that a 1-tuple of +argument types must be written with a trailing comma. For example, to call the `getenv` function +to get a pointer to the value of an environment variable, one makes a call like this: ```julia-repl -julia> path = ccall((:getenv, "libc"), Cstring, (Cstring,), "SHELL") +julia> path = ccall(:getenv, Cstring, (Cstring,), "SHELL") Cstring(@0x00007fff5fbffc45) julia> unsafe_string(path) @@ -72,12 +86,11 @@ julia> (Cstring,) ```julia function getenv(var::AbstractString) - val = ccall((:getenv, "libc"), - Cstring, (Cstring,), var) + val = ccall(:getenv, Cstring, (Cstring,), var) if val == C_NULL error("getenv: undefined variable: ", var) end - unsafe_string(val) + return unsafe_string(val) end ``` @@ -91,15 +104,19 @@ julia> getenv("FOOBAR") getenv: undefined variable: FOOBAR ``` -这有一个稍微复杂一点的例子,功能是发现本机的主机名: +Here is a slightly more complex example that discovers the local machine's hostname. +In this example, the networking library code is assumed to be in a shared library named "libc". +In practice, this function is usually part of the C standard library, and so the "libc" +portion should be omitted, but we wish to show here the usage of this syntax. ```julia function gethostname() - hostname = Vector{UInt8}(undef, 128) - ccall((:gethostname, "libc"), Int32, - (Ptr{UInt8}, Csize_t), - hostname, sizeof(hostname)) - hostname[end] = 0; # ensure null-termination + hostname = Vector{UInt8}(undef, 256) # MAXHOSTNAMELEN + err = ccall((:gethostname, "libc"), Int32, + (Ptr{UInt8}, Csize_t), + hostname, sizeof(hostname)) + Base.systemerror("gethostname", err != 0) + hostname[end] = 0 # ensure null-termination return unsafe_string(pointer(hostname)) end ``` @@ -115,15 +132,21 @@ Julia中类似的内存分配通常是通过创建一个未初始化的数组并 typedef returntype (*functiontype)(argumenttype, ...) ``` -宏 [`@cfunction`](@ref) 生成可兼容C的函数指针,来调用Julia函数。 [`@cfunction`](@ref) 的参数如下: +The macro [`@cfunction`](@ref) generates the C-compatible function pointer for a call to a +Julia function. The arguments to [`@cfunction`](@ref) are: -1. 一个Julia函数 -2. 返回类型 -3. 一个与输入类型相同的字面量元组 +1. A Julia function +2. The function's return type +3. A tuple of input types, corresponding to the function signature -与 ccall 一样,所有的参数将会在编译期执行,也就是包含的函数被定义时执行。 +!!! note + As with `ccall`, the return type and tuple of input types must be literal constants. -目前仅支持平台默认的C调用转换。这意味着`@cfunction`产生的指针不能在WINAPI需要`stdcall`的32位Windows上使用,但是能在(`stdcall`独立于C调用转换的)WIN64上使用。 +!!! note + Currently, only the platform-default C calling convention is supported. This means that + `@cfunction`-generated pointers cannot be used in calls where WINAPI expects `stdcall` + function on 32-bit Windows, but can be used on WIN64 (where `stdcall` is unified with the + C calling convention). 一个典型的例子就是标准C库函数`qsort`,定义为: @@ -132,7 +155,14 @@ void qsort(void *base, size_t nmemb, size_t size, int (*compare)(const void*, const void*)); ``` -`base` 是一个指向长度为`nmemb`,每个元素大小为`size`的数组的指针。`compare` 是一个回调函数,它接受指向两个元素`a`和`b`的指针并根据`a`应该排在`b`的前面或者后面返回一个大于或小于0的整数(如果在顺序无关紧要时返回0)。现在,假设在Julia中有一整个1维数组`A`的值我们希望用`qsort`(或者Julia的内置`sort`函数)函数进行排序。在我们关心调用`qsort`及其参数传递之前,我们需要写一个为每对值之间进行比较的函数(定义`<`)。 +The `base` argument is a pointer to an array of length `nmemb`, with elements of `size` bytes +each. `compare` is a callback function which takes pointers to two elements `a` and `b` and returns +an integer less/greater than zero if `a` should appear before/after `b` (or zero if any order +is permitted). + +Now, suppose that we have a 1d array `A` of values in Julia that we want to sort +using the `qsort` function (rather than Julia's built-in `sort` function). Before we worry about +calling `qsort` and passing arguments, we need to write a comparison function: ```jldoctest mycompare julia> function mycompare(a, b)::Cint @@ -141,9 +171,10 @@ julia> function mycompare(a, b)::Cint mycompare (generic function with 1 method) ``` -注意,我们需要小心返回值的类型:`qsort`期待一个返回C语言`int`类型的函数,所以我们需要标出这个函数的返回值类型来确保它返回`Cint`。 +``qsort`` expects a comparison function that return a C ``int``, so we annotate the return type +to be ``Cint``. -为了传递这个函数给C,我们可以用宏 `@cfunction` 取得它的地址: +In order to pass this function to C, we obtain its address using the macro `@cfunction`: ```jldoctest mycompare julia> mycompare_c = @cfunction(mycompare, Cint, (Ref{Cdouble}, Ref{Cdouble})); @@ -173,12 +204,14 @@ julia> A ``` As can be seen, `A` is changed to the sorted array `[-2.7, 1.3, 3.1, 4.4]`. Note that Julia -knows how to convert an array into a `Ptr{Cdouble}`, how to compute the size of a type in bytes -(identical to C's `sizeof` operator), and so on. For fun, try inserting a `println("mycompare($a, $b)")` -line into `mycompare`, which will allow you to see the comparisons that `qsort` is performing -(and to verify that it is really calling the Julia function that you passed to it). +[takes care of converting the array to a `Ptr{Cdouble}`](@ref automatic-type-conversion)), computing +the size of the element type in bytes, and so on. + +For fun, try inserting a `println("mycompare($a, $b)")` line into `mycompare`, which will allow +you to see the comparisons that `qsort` is performing (and to verify that it is really calling +the Julia function that you passed to it). -## Mapping C Types to Julia +## [Mapping C Types to Julia](@id mapping-c-types-to-julia) It is critical to exactly match the declared C type with its declaration in Julia. Inconsistencies can cause code that works correctly on one system to fail or produce indeterminate results on @@ -186,10 +219,9 @@ a different system. Note that no C header files are used anywhere in the process of calling C functions: you are responsible for making sure that your Julia types and call signatures accurately reflect those in the C header -file. (The [Clang package](https://github.com/ihnorton/Clang.jl) can be used to auto-generate -Julia code from a C header file.) +file.[^2] -### Auto-conversion: +### [Automatic Type Conversion](@id automatic-type-conversion) Julia automatically inserts calls to the [`Base.cconvert`](@ref) function to convert each argument to the specified type. For example, the following call: @@ -215,9 +247,9 @@ For example, this is used to convert an `Array` of objects (e.g. strings) to an converting an object to a native pointer can hide the object from the garbage collector, causing it to be freed prematurely. -### 类型对应关系 +### Type Correspondences -首先来复习一下 Julia 类型相关的术语: +First, let's review some relevant Julia type terminology: | 语法 / 关键字 | 例子 | 描述 | |:----------------------------- |:------------------------------------------- |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | @@ -284,7 +316,7 @@ same: 这在写可移植代码时将非常有用(注意到,C中的 `int` 类型和Julia的 `Int` 不同)。 -**与系统独立的:** +**System Independent Types** | C 类型 | Fortran 类型 | 标准 Julia 别名 | Julia 基本类型 | |:------------------------------------------------------- |:------------------------ |:-------------------- |:-------------------------------------------------------------------------------------------------------------- | @@ -325,7 +357,7 @@ to skip the check, you can use `Ptr{UInt8}` as the argument type. `Cstring` can the [`ccall`](@ref) return type, but in that case it obviously does not introduce any extra checks and is only meant to improve readability of the call. -**依赖于系统的:** +**System Dependent Types** | C 类型 | 标准 Julia 别名 | Julia 基本类型 | |:--------------- |:-------------------- |:-------------------------------------------- | @@ -355,11 +387,11 @@ checks and is only meant to improve readability of the call. (`void`) but do return, use `Cvoid` instead. !!! note - For `wchar_t*` arguments, the Julia type should be [`Cwstring`](@ref) (if the C routine expects a NUL-terminated - string) or `Ptr{Cwchar_t}` otherwise. Note also that UTF-8 string data in Julia is internally - NUL-terminated, so it can be passed to C functions expecting NUL-terminated data without making - a copy (but using the `Cwstring` type will cause an error to be thrown if the string itself contains - NUL characters). + For `wchar_t*` arguments, the Julia type should be [`Cwstring`](@ref) (if the C routine expects a + NUL-terminated string) or `Ptr{Cwchar_t}` otherwise. Note also that UTF-8 string data in Julia is + internally NUL-terminated, so it can be passed to C functions expecting NUL-terminated data without + making a copy (but using the `Cwstring` type will cause an error to be thrown if the string itself + contains NUL characters). !!! note C functions that take an argument of the type `char**` can be called by using a `Ptr{Ptr{UInt8}}` @@ -394,7 +426,7 @@ checks and is only meant to improve readability of the call. ```julia str1 = "foo" str2 = "bar" - ccall(:test, Void, (Ptr{UInt8}, Ptr{UInt8}, Csize_t, Csize_t), + ccall(:test, Cvoid, (Ptr{UInt8}, Ptr{UInt8}, Csize_t, Csize_t), str1, str2, sizeof(str1), sizeof(str2)) ``` @@ -408,7 +440,7 @@ checks and is only meant to improve readability of the call. !!! note A C function declared to return `Cvoid` will return the value `nothing` in Julia. -### Struct Type correspondences +### Struct Type Correspondences Composite types, aka `struct` in C or `TYPE` in Fortran90 (or `STRUCTURE` / `RECORD` in some variants of F77), can be mirrored in Julia by creating a `struct` definition with the same @@ -422,29 +454,32 @@ are not possible in the translation to Julia. Packed structs and union declarations are not supported by Julia. -You can get a near approximation of a `union` if you know, a priori, the field that will have +You can get an approximation of a `union` if you know, a priori, the field that will have the greatest size (potentially including padding). When translating your fields to Julia, declare the Julia field to be only of that type. -Arrays of parameters can be expressed with `NTuple`: +Arrays of parameters can be expressed with `NTuple`. For example, the struct in C notation written as -in C: ```c struct B { int A[3]; }; + b_a_2 = B.A[2]; ``` -in Julia: + +can be written in Julia as + ```julia struct B A::NTuple{3, Cint} end -b_a_2 = B.A[3] # 请注意索引上的不同(Julia 中为 1-based 索引,C 中为 0-based 索引) + +b_a_2 = B.A[3] # note the difference in indexing (1-based in Julia, 0-based in C) ``` -Arrays of unknown size (C99-compliant variable length structs specified by `[]` or `[0]`) are not directly supported. -Often the best way to deal with these is to deal with the byte offsets directly. +Arrays of unknown size (C99-compliant variable length structs specified by `[]` or `[0]`) are not directly +supported. Often the best way to deal with these is to deal with the byte offsets directly. For example, if a C library declared a proper string type and returned a pointer to it: ```c @@ -530,7 +565,7 @@ work on hosts without AVX support. Memory allocation and deallocation of such objects must be handled by calls to the appropriate cleanup routines in the libraries being used, just like in any C program. Do not try to free an object received from a C library with [`Libc.free`](@ref) in Julia, as this may result in the `free` function -being called via the wrong `libc` library and cause Julia to crash. The reverse (passing an object +being called via the wrong library and cause the process to abort. The reverse (passing an object allocated in Julia to be freed by an external library) is equally invalid. ### 何时使用 T、Ptr{T} 以及 Ref{T} @@ -539,7 +574,7 @@ In Julia code wrapping calls to external C routines, ordinary (non-pointer) data to be of type `T` inside the [`ccall`](@ref), as they are passed by value. For C code accepting pointers, [`Ref{T}`](@ref) should generally be used for the types of input arguments, allowing the use of pointers to memory managed by either Julia or C through the implicit call to [`Base.cconvert`](@ref). - In contrast, pointers returned by the C function called should be declared to be of output type +In contrast, pointers returned by the C function called should be declared to be of output type [`Ptr{T}`](@ref), reflecting that the memory pointed to is managed by C only. Pointers contained in C structs should be represented as fields of type `Ptr{T}` within the corresponding Julia struct types designed to mimic the internal structure of corresponding C structs. @@ -629,8 +664,8 @@ For translating a C return type to Julia: * If the memory is already owned by Julia, or is an `isbits` type, and is known to be non-null: * `Ref{T}`, where `T` is the Julia type corresponding to `T` - * a return type of `Ref{Any}` is invalid, it should either be `Any` (corresponding to `jl_value_t*`) - or `Ptr{Any}` (corresponding to `jl_value_t**`) + * a return type of `Ref{Any}` is invalid, it should either be `Any` (corresponding to + `jl_value_t*`) or `Ptr{Any}` (corresponding to `jl_value_t**`) * C **MUST NOT** modify the memory returned via `Ref{T}` if `T` is an `isbits` type * If the memory is owned by C: @@ -655,39 +690,9 @@ ccall(:foo, Cvoid, (Ref{Cint}, Ref{Cfloat}), width, range) Upon return, the contents of `width` and `range` can be retrieved (if they were changed by `foo`) by `width[]` and `range[]`; that is, they act like zero-dimensional arrays. -### Special Reference Syntax for ccall (deprecated): - -The `&` syntax is deprecated, use the `Ref{T}` argument type instead. +## C Wrapper Examples -A prefix `&` is used on an argument to [`ccall`](@ref) to indicate that a pointer to a scalar -argument should be passed instead of the scalar value itself (required for all Fortran function -arguments, as noted above). The following example computes a dot product using a BLAS function. - -```julia -function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) - @assert length(DX) == length(DY) - n = length(DX) - incx = incy = 1 - product = ccall((:ddot_, "libLAPACK"), - Float64, - (Ref{Int32}, Ptr{Float64}, Ref{Int32}, Ptr{Float64}, Ref{Int32}), - n, DX, incx, DY, incy) - return product -end -``` - -The meaning of prefix `&` is not quite the same as in C. In particular, any changes to the referenced -variables will not be visible in Julia unless the type is mutable (declared via `mutable struct`). However, -even for immutable structs it will not cause any harm for called functions to attempt such modifications -(that is, writing through the passed pointers). Moreover, `&` may be used with any expression, -such as `&0` or `&f(x)`. - -When a scalar value is passed with `&` as an argument of type `Ptr{T}`, the value will first be -converted to type `T`. - -## Some Examples of C Wrappers - -Here is a simple example of a C wrapper that returns a `Ptr` type: +Let's start with a simple example of a C wrapper that returns a `Ptr` type: ```julia mutable struct gsl_permutation @@ -715,12 +720,12 @@ function `gsl_permutation_alloc`. As user code never has to look inside the `gsl struct, the corresponding Julia wrapper simply needs a new type declaration, `gsl_permutation`, that has no internal fields and whose sole purpose is to be placed in the type parameter of a `Ptr` type. The return type of the [`ccall`](@ref) is declared as `Ptr{gsl_permutation}`, since -the memory allocated and pointed to by `output_ptr` is controlled by C (and not Julia). +the memory allocated and pointed to by `output_ptr` is controlled by C. The input `n` is passed by value, and so the function's input signature is simply declared as `(Csize_t,)` without any `Ref` or `Ptr` necessary. (If the wrapper was calling a Fortran function instead, the corresponding function input -signature should instead be `(Ref{Csize_t},)`, since Fortran variables are +signature would instead be `(Ref{Csize_t},)`, since Fortran variables are passed by pointers.) Furthermore, `n` can be any type that is convertible to a `Csize_t` integer; the [`ccall`](@ref) implicitly calls [`Base.cconvert(Csize_t, n)`](@ref). @@ -743,11 +748,12 @@ end Here, the input `p` is declared to be of type `Ref{gsl_permutation}`, meaning that the memory that `p` points to may be managed by Julia or by C. A pointer to memory allocated by C should be of type `Ptr{gsl_permutation}`, but it is convertible using [`Base.cconvert`](@ref) and therefore -can be used in the same (covariant) context of the input argument to a [`ccall`](@ref). A pointer -to memory allocated by Julia must be of type `Ref{gsl_permutation}`, to ensure that the memory -address pointed to is valid and that Julia's garbage collector manages the chunk of memory pointed -to correctly. Therefore, the `Ref{gsl_permutation}` declaration allows pointers managed by C or -Julia to be used. + +Now if you look closely enough at this example, you may notice that it is incorrect, given our explanation +above of preferred declaration types. Do you see it? The function we are calling is going to free the +memory. This type of operation cannot be given a Julia object (it will crash or cause memory corruption). +Therefore, it may be preferable to declare the `p` type as `Ptr{gsl_permutation }`, to make it harder for the +user to mistakenly pass another sort of object there than one obtained via `gsl_permutation_alloc`. If the C wrapper never expects the user to pass pointers to memory managed by Julia, then using `p::Ptr{gsl_permutation}` for the method signature of the wrapper and similarly in the [`ccall`](@ref) @@ -778,19 +784,33 @@ end ``` The C function wrapped returns an integer error code; the results of the actual evaluation of -the Bessel J function populate the Julia array `result_array`. This variable can only be used -with corresponding input type declaration `Ref{Cdouble}`, since its memory is allocated and managed -by Julia, not C. The implicit call to [`Base.cconvert(Ref{Cdouble}, result_array)`](@ref) unpacks +the Bessel J function populate the Julia array `result_array`. This variable is declared as a +`Ref{Cdouble}`, since its memory is allocated and managed by Julia. The implicit call to +[`Base.cconvert(Ref{Cdouble}, result_array)`](@ref) unpacks the Julia pointer to a Julia array data structure into a form understandable by C. -Note that for this code to work correctly, `result_array` must be declared to be of type `Ref{Cdouble}` -and not `Ptr{Cdouble}`. The memory is managed by Julia and the `Ref` signature alerts Julia's -garbage collector to keep managing the memory for `result_array` while the [`ccall`](@ref) executes. -If `Ptr{Cdouble}` were used instead, the [`ccall`](@ref) may still work, but Julia's garbage -collector would not be aware that the memory declared for `result_array` is being used by the -external C function. As a result, the code may produce a memory leak if `result_array` never gets -freed by the garbage collector, or if the garbage collector prematurely frees `result_array`, -the C function may end up throwing an invalid memory access exception. +## Fortran Wrapper Example + +The following example utilizes ccall to call a function in a common Fortran library (libBLAS) to +computes a dot product. Notice that the argument mapping is a bit different here than above, as +we need to map from Julia to Fortran. On every argument type, we specify `Ref` or `Ptr`. This +mangling convention may be specific to your fortran compiler and operating system, and is likely +undocumented. However, wrapping each in a `Ref` (or `Ptr`, where equivalent) is a frequent +requirement of Fortran compiler implementations: + +```julia +function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) + @assert length(DX) == length(DY) + n = length(DX) + incx = incy = 1 + product = ccall((:ddot_, "libLAPACK"), + Float64, + (Ref{Int32}, Ptr{Float64}, Ref{Int32}, Ptr{Float64}, Ref{Int32}), + n, DX, incx, DY, incy) + return product +end +``` + ## 垃圾回收安全 @@ -805,8 +825,9 @@ the C library notifies you that it is finished with them. Whenever you have created a pointer to Julia data, you must ensure the original data exists until you are done with using the pointer. Many methods in Julia such as [`unsafe_load`](@ref) and [`String`](@ref) make copies of data instead of taking ownership of the buffer, so that it is -safe to free (or alter) the original data without affecting Julia. A notable exception is [`unsafe_wrap`](@ref) -which, for performance reasons, shares (or can be told to take ownership of) the underlying buffer. +safe to free (or alter) the original data without affecting Julia. A notable exception is +[`unsafe_wrap`](@ref) which, for performance reasons, shares (or can be told to take ownership of) the +underlying buffer. The garbage collector does not guarantee any order of finalization. That is, if `a` contained a reference to `b` and both `a` and `b` are due for garbage collection, there is no guarantee @@ -829,8 +850,8 @@ with `$`). For this reason, `eval` is typically only used to form top-level defi when wrapping libraries that contain many similar functions. A similar example can be constructed for [`@cfunction`](@ref). -However, doing this will also be very slow and leak memory, so you should usually avoid this and instead keep reading. -The next section discusses how to use indirect calls to efficiently accomplish a similar effect. +However, doing this will also be very slow and leak memory, so you should usually avoid this and instead keep +reading. The next section discusses how to use indirect calls to efficiently accomplish a similar effect. ## 非直接调用 @@ -897,10 +918,10 @@ and load in the new changes. One can either restart Julia or use the `Libdl` functions to manage the library explicitly, such as: ```julia -lib = Libdl.dlopen("./my_lib.so") # 显式打开库 -sym = Libdl.dlsym(lib, :my_fcn) # 获得用于调用函数的符号 -ccall(sym, ...) # 直接用指针 `sym` 而不是 (symbol, library) 元组,其余参数保持不变 -Libdl.dlclose(lib) # 显式关闭库 +lib = Libdl.dlopen("./my_lib.so") # Open the library explicitly. +sym = Libdl.dlsym(lib, :my_fcn) # Get a symbol for the function to call. +ccall(sym, ...) # Use the pointer `sym` instead of the (symbol, library) tuple (remaining arguments are the +same). Libdl.dlclose(lib) # Close the library explicitly. ``` Note that when using `ccall` with the tuple input @@ -911,8 +932,8 @@ and it may not be explicitly closed. The second argument to [`ccall`](@ref) can optionally be a calling convention specifier (immediately preceding return type). Without any specifier, the platform-default C calling convention is used. -Other supported conventions are: `stdcall`, `cdecl`, `fastcall`, and `thiscall` (no-op on 64-bit Windows). For example (from -`base/libc.jl`) we see the same `gethostname`[`ccall`](@ref) as above, but with the correct +Other supported conventions are: `stdcall`, `cdecl`, `fastcall`, and `thiscall` (no-op on 64-bit Windows). +For example (from `base/libc.jl`) we see the same `gethostname`[`ccall`](@ref) as above, but with the correct signature for Windows: ```julia @@ -950,6 +971,15 @@ Ptr{Int32} @0x00007f418d0816b8 The result is a pointer giving the address of the value. The value can be manipulated through this pointer using [`unsafe_load`](@ref) and [`unsafe_store!`](@ref). +!!! note + This `errno` symbol may not be found in a library named "libc", as this is an implementation detail of + your system compiler. Typically standard library symbols should be accessed just by name, + allowing the compiler to fill in the correct one. + Also, however, the `errno` symbol shown in this example is special in most compilers, and so the value + seen here is probably not what you expect or want. Compiling the equivalent code in C on any + multi-threaded-capable system would typically actually call a different function (via macro preprocessor + overloading), and may give a different result than the legacy value printed here. + ## Accessing Data through a Pointer The following methods are described as "unsafe" because a bad pointer or type declaration can @@ -973,12 +1003,14 @@ back to a Julia object reference by [`unsafe_pointer_to_objref(ptr)`](@ref). (Ju can be converted to `jl_value_t*` pointers, as `Ptr{Cvoid}`, by calling [`pointer_from_objref(v)`](@ref).) The reverse operation (writing data to a `Ptr{T}`), can be performed using [`unsafe_store!(ptr, value, [index])`](@ref). -Currently, this is only supported for primitive types or other pointer-free (`isbits`) immutable struct types. +Currently, this is only supported for primitive types or other pointer-free (`isbits`) immutable struct +types. Any operation that throws an error is probably currently unimplemented and should be posted as a bug so that it can be resolved. -If the pointer of interest is a plain-data array (primitive type or immutable struct), the function [`unsafe_wrap(Array, ptr,dims, own = false)`](@ref) +If the pointer of interest is a plain-data array (primitive type or immutable struct), the function +[`unsafe_wrap(Array, ptr,dims, own = false)`](@ref) may be more useful. The final parameter should be true if Julia should "take ownership" of the underlying buffer and call `free(ptr)` when the returned `Array` object is finalized. If the `own` parameter is omitted or false, the caller must ensure the buffer remains in existence until @@ -1012,3 +1044,12 @@ wait(cond) ## C++ 如需要直接易用的C++接口,即直接用Julia写封装代码,请参考 [Cxx](https://github.com/Keno/Cxx.jl)。如需封装C++库的工具,即用C++写封装/胶水代码,请参考[CxxWrap](https://github.com/JuliaInterop/CxxWrap.jl)。 + + + +[^1]: Non-library function calls in both C and Julia can be inlined and thus may have + even less overhead than calls to shared library functions. + The point above is that the cost of actually doing foreign function call is about the same as doing a call in either native language. + +[^2]: The [Clang package](https://github.com/ihnorton/Clang.jl) can be used to auto-generate Julia code + from a C header file. diff --git a/zh_CN/doc/src/manual/code-loading.md b/zh_CN/doc/src/manual/code-loading.md index 196b1dd0..b56a0aad 100644 --- a/zh_CN/doc/src/manual/code-loading.md +++ b/zh_CN/doc/src/manual/code-loading.md @@ -170,16 +170,16 @@ graph[UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1")][:Priv] 在上述样例清单文件中,为找到首个 `Priv` 包的路径——该包 UUID 为 `ba13f791-ae1d-465a-978b-69c3ad90f72b`——Julia 寻找其在清单中的节(stanza)。发现其有 路径` 入口,查看 `App` 项目目录中相关的 `deps/Priv`——不妨设`App` 代码在 `/home/me/projects/App` 中—则 Julia 发现 `/home/me/projects/App/deps/Priv` 存在,并因此从中加载 `Priv`。 -If, on the other hand, Julia was loading the *other* `Priv` package—the one with UUID `2d15fe94-a1f7-436c-a4d8-07a9a496e01c`—it finds its stanza in the manifest, see that it does *not* have a `path` entry, but that it does have a `git-tree-sha1` entry. It then computes the `slug` for this UUID/SHA-1 pair, which is `HDkrT` (the exact details of this computation aren't important, but it is consistent and deterministic). This means that the path to this `Priv` package will be `packages/Priv/HDkrT/src/Priv.jl` in one of the package depots. Suppose the contents of `DEPOT_PATH` is `["/home/me/.julia", "/usr/local/julia"]`, then Julia will look at the following paths to see if they exist: +另一方面,如果Julia加载的是带有*other* `Priv` 包——即UUID为`2d15fe94-a1f7-436c-a4d8-07a9a496e01c`——它在清单中找到了它的节,请注意它*没有*`path`条目,但是它有一个`git-tree-sha1` 条目。然后计算这个`slug` 的UUID/SHA-1对,具体是`HDkrT`(这个计算的确切细节并不重要,但它是始终一致的和确定的)。这意味着这个`Priv`包的路径`packages/Priv/HDkrT/src/Priv.jl`将在其中一个包仓库中。假设`DEPOT_PATH` 的内容是`["/home/me/.julia", "/usr/local/julia"]`,Julia将根据下面的路径来查看它们是否存在: 1. `/home/me/.julia/packages/Priv/HDkrT` 2. `/usr/local/julia/packages/Priv/HDkrT` -Julia uses the first of these that exists to try to load the public `Priv` package from the file `packages/Priv/HDKrT/src/Priv.jl` in the depot where it was found. +Julia使用以上路径信息在仓库里依次查找 `packages/Priv/HDKrT/src/Priv.jl `文件,并从第一个查找到的文件中加载公共的 `Priv `包。 -Here is a representation of a possible paths map for our example `App` project environment, -as provided in the Manifest given above for the dependency graph, -after searching the local file system: +这是我们的示例App项目环境的可能路径映射的表示, + 如上面Manifest 中所提供的依赖关系图, +在 搜索本地文件系统后: ```julia paths = Dict( @@ -202,39 +202,39 @@ paths = Dict( ) ``` -This example map includes three different kinds of package locations (the first and third are part of the default load path): +这个例子包含三种不同类型的包位置信息(第一个和第三个是默认加载路径的一部分) -1. The private `Priv` package is "[vendored](https://stackoverflow.com/a/35109534)" inside the `App` repository. -2. The public `Priv` and `Zebra` packages are in the system depot, where packages installed and managed by the system administrator live. These are available to all users on the system. -3. The `Pub` package is in the user depot, where packages installed by the user live. These are only available to the user who installed them. +1. 私有 `Priv` 包 "[vendored](https://stackoverflow.com/a/35109534)"包括在`App`仓库中。 +2. 公共 `Priv` 与 `Zebra` 包位于系统仓库,系统管理员在此对相关包进行实时安装与管理。这些包允许系统上的所有用户使用。 +3. `Pub` 包位于用户仓库,用户实时安装的包都储存在此。 这些包仅限原安装用户使用。 ### 包目录 -包目录提供一种更简单的,不能处理名称冲突的环境。 +包目录提供了一种更简单的环境,但不能处理名称冲突。在包目录中, 顶层包集合是“类似”包的子目录集合。“X”包存在于包目录中的条件,是目录包含下列“入口点”文件之一: - `X.jl` - `X/src/X.jl` - `X.jl/src/X.jl` -Which dependencies a package in a package directory can import depends on whether the package contains a project file: +包目录中的包可以导入哪些依赖项,取决于该包是否含有项目文件: -* If it has a project file, it can only import those packages which are identified in the `[deps]` section of the project file. -* If it does not have a project file, it can import any top-level package—i.e. the same packages that can be loaded in `Main` or the REPL. +* 如果它有一个项目文件,那么它只能导入那些在项目文件的`[deps]` 部分中已标识的包。 +* 如果没有项目文件,它可以导入任何顶层包,即与在`Main` 或者 REPL中可加载的包相同。 -**The roots map** is determined by examining the contents of the package directory to generate a list of all packages that exist. -Additionally, a UUID will be assigned to each entry as follows: For a given package found inside the folder `X`... +**根图**是根据包目录的所有内容而形成的一个列表,包含所有已存在的包。 +此外,一个UUID 将被赋予给每一个条目,例如对一个在文件夹`X`中找到的包 -1. If `X/Project.toml` exists and has a `uuid` entry, then `uuid` is that value. -2. If `X/Project.toml` exists and but does *not* have a top-level UUID entry, `uuid` is a dummy UUID generated by hashing the canonical (real) path to `X/Project.toml`. -3. Otherwise (if `Project.toml` does not exist), then `uuid` is the all-zero [nil UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Nil_UUID). +1. 如果`X/Project.toml`文件存在并且有一个`uuid` 条目,那么这个 `uuid`就是上述所要赋予的值。 +2. 如果`X/Project.toml`文件存在,但*没有*包含一个顶层UUID条目, 该`uuid`将是一个虚构的UUID,是对`X/Project.toml`文件所在的规范(真实的)路径信息进行哈希处理而生成。 +3. 否则(如果`Project.toml`文件不存在), `uuid`将是一个全零值 [nil UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Nil_UUID)。 -**The dependency graph** of a project directory is determined by the presence and contents of project files in the subdirectory of each package. The rules are: +项目目录的**依赖关系图**是根据每个包的子目录中其项目文件的存在与否以及内容而形成。规则是: -- If a package subdirectory has no project file, then it is omitted from graph and import statements in its code are treated as top-level, the same as the main project and REPL. -- If a package subdirectory has a project file, then the graph entry for its UUID is the `[deps]` map of the project file, which is considered to be empty if the section is absent. +- 如果包子目录没有项目文件,则在该图中忽略它, 其代码中的import语句按顶层处理,与main项目和REPL相同。 +- 如果包子目录有一个项目文件,那么图条目的UUID是项目文件的`[deps]`映射, 如果该信息项不存在,则视为空。 -As an example, suppose a package directory has the following structure and content: +作为一个例子,假设包目录具有以下结构和内容: ``` Aardvark/ @@ -269,7 +269,7 @@ Dingo/ # no imports ``` -Here is a corresponding roots structure, represented as a dictionary: +下面是相应的根结构,表示为字典: ```julia roots = Dict( @@ -280,7 +280,7 @@ roots = Dict( ) ``` -Here is the corresponding graph structure, represented as a dictionary: +下面是对应的图结构,表示为字典: ```julia graph = Dict( @@ -298,22 +298,24 @@ graph = Dict( ) ``` -A few general rules to note: +需要注意的一些概括性规则: -1. A package without a project file can depend on any top-level dependency, and since every package in a package directory is available at the top-level, it can import all packages in the environment. -2. A package with a project file cannot depend on one without a project file since packages with project files can only load packages in `graph` and packages without project files do not appear in `graph`. -3. A package with a project file but no explicit UUID can only be depended on by packages without project files since dummy UUIDs assigned to these packages are strictly internal. +1. 缺少项目文件的包能依赖于任何顶层依赖项, 并且由于包目录中的每个包在顶层依赖中可用,因此它可以导入在环境中的所有包。 +2. 含有项目文件的包不能依赖于缺少项目文件的包。 因为有项目文件的包只能加载那些在`graph`中的包,而没有项目文件的包不会出现在`graph`。 +3. 具有项目文件但没有明确UUID的包只能被由没有项目文件的包所依赖, since dummy UUIDs assigned to these packages are strictly internal. +,因为赋予给这些包的虚构UUID全是项目内部的。 Observe the following specific instances of these rules in our example: +请注意以下我们例子中的规则具体实例: -* `Aardvark` can import on any of `Bobcat`, `Cobra` or `Dingo`; it does import `Bobcat` and `Cobra`. -* `Bobcat` can and does import both `Cobra` and `Dingo`, which both have project files with UUIDs and are declared as dependencies in `Bobcat`'s `[deps]` section. -* `Bobcat` cannot depend on `Aardvark` since `Aardvark` does not have a project file. -* `Cobra` can and does import `Dingo`, which has a project file and UUID, and is declared as a dependency in `Cobra`'s `[deps]` section. -* `Cobra` cannot depend on `Aardvark` or `Bobcat` since neither have real UUIDs. -* `Dingo` cannot import anything because it has a project file without a `[deps]` section. +* `Aardvark` 包可以导入`Bobcat`、`Cobra` 或`Dingo`中的所有包;它确实导入`Bobcat` and `Cobra`包. +* `Bobcat` 包能导入`Cobra`与`Dingo`包。因为它们都有带有UUID的项目文件,并在`Bobcat`包的`[deps]`信息项声明为依赖项。 +* `Bobcat`包不能依赖于`Aardvark`包,因为`Aardvark`包缺少项目文件。 +* `Cobra`包能导入`Dingo`包。因为`Dingo`包有项目文件和UUID,并在`Cobra`的`[deps]` 信息项中声明为依赖项。 +* `Cobra`包不能依赖`Aardvark`或`Bobcat`包, 因为两者都没有真实的UUID。 +* `Dingo`包不能导入任何包,因为它的项目文件中缺少`[deps]`信息项。 -**The paths map** in a package directory is simple: it maps subdirectory names to their corresponding entry-point paths. In other words, if the path to our example project directory is `/home/me/animals` then the `paths` map could be represented by this dictionary: +包目录中的**路径映射**很简单: 它将子目录名映射到相应的入口点路径。换句话说,如果指向我们示例项目目录的路径是`/home/me/animals`,那么`路径`映射可以用此字典表示: ```julia paths = Dict( @@ -328,13 +330,13 @@ paths = Dict( ) ``` -Since all packages in a package directory environment are, by definition, subdirectories with the expected entry-point files, their `paths` map entries always have this form. +根据定义,包目录环境中的所有包都是具有预期入口点文件的子目录,因此它们的`路径` 映射条目始终具有此格式。 -### Environment stacks +### 环境堆栈 -The third and final kind of environment is one that combines other environments by overlaying several of them, making the packages in each available in a single composite environment. These composite environments are called *environment stacks*. The Julia `LOAD_PATH` global defines an environment stack—the environment in which the Julia process operates. If you want your Julia process to have access only to the packages in one project or package directory, make it the only entry in `LOAD_PATH`. It is often quite useful, however, to have access to some of your favorite tools—standard libraries, profilers, debuggers, personal utilities, etc.—even if they are not dependencies of the project you're working on. By adding an environment containing these tools to the load path, you immediately have access to them in top-level code without needing to add them to your project. +第三种也是最后一种环境是通过覆盖其中的几个环境来组合其他环境,使每个环境中的包在单个组合环境中可用。这些复合环境称为*环境堆栈*。Julia的`LOAD_PATH`全局定义一个环境堆栈——Julia进程在其中运行的环境。如果希望Julia进程只能访问一个项目或包目录中的包,请将其设置为`LOAD_PATH`中的唯一条目。然而,访问一些您喜爱的工具(标准库、探查器、调试器、个人实用程序等)通常是非常有用的,即使它们不是您正在处理的项目的依赖项。通过将包含这些工具的环境添加到加载路径,您可以立即在顶层代码中访问它们,而无需将它们添加到项目中。 -The mechanism for combining the roots, graph and paths data structures of the components of an environment stack is simple: they are merged as dictionaries, favoring earlier entries over later ones in the case of key collisions. In other words, if we have `stack = [env₁, env₂, …]` then we have: +组合环境堆栈组件中根、图和路径的数据结构的机制很简单:它们被作为字典进行合并, 在发生键冲突时,优先使用前面的条目而不是后面的条目。换言之,如果我们有`stack = [env₁, env₂, …]`,那么我们有: ```julia roots = reduce(merge, reverse([roots₁, roots₂, …])) @@ -342,13 +344,13 @@ graph = reduce(merge, reverse([graph₁, graph₂, …])) paths = reduce(merge, reverse([paths₁, paths₂, …])) ``` -The subscripted `rootsᵢ`, `graphᵢ` and `pathsᵢ` variables correspond to the subscripted environments, `envᵢ`, contained in `stack`. The `reverse` is present because `merge` favors the last argument rather than first when there are collisions between keys in its argument dictionaries. There are a couple of noteworthy features of this design: +带下标的 `rootsᵢ`, `graphᵢ` and `pathsᵢ`变量对应于在`stack`中包含的下标环境变量`envᵢ`。 使用`reverse` 是因为当参数字典中的键之间发生冲突时,使`merge` 倾向于使用最后一个参数,而不是第一个参数。这种设计有几个值得注意的特点: -1. The *primary environment*—i.e. the first environment in a stack—is faithfully embedded in a stacked environment. The full dependency graph of the first environment in a stack is guaranteed to be included intact in the stacked environment including the same versions of all dependencies. -2. Packages in non-primary environments can end up using incompatible versions of their dependencies even if their own environments are entirely compatible. This can happen when one of their dependencies is shadowed by a version in an earlier environment in the stack (either by graph or path, or both). +1. *主环境*——即堆栈中的第一个环境,被准确地嵌入到堆栈环境中。堆栈中第一个环境的完整依赖关系图是必然被完整包括在含有所有相同版本的依赖项的堆栈环境中。 +2. 非主环境中的包能最终使用与其依赖项不兼容的版本,即使它们自己的环境是完全兼容。这种情况可能发生,当它们的一个依赖项被堆栈(通过图或路径,或两者)中某个早期环境中的版本所覆盖。 -Since the primary environment is typically the environment of a project you're working on, while environments later in the stack contain additional tools, this is the right trade-off: it's better to break your development tools but keep the project working. When such incompatibilities occur, you'll typically want to upgrade your dev tools to versions that are compatible with the main project. +由于主环境通常是您正在处理的项目所在的环境,而堆栈中稍后的环境包含其他工具, 因此这是正确的权衡:最好改进您的开发工具,但保持项目能工作。当这种不兼容发生时,你通常要将开发工具升级到与主项目兼容的版本。 ## 总结 -Federated package management and precise software reproducibility are difficult but worthy goals in a package system. In combination, these goals lead to a more complex package loading mechanism than most dynamic languages have, but it also yields scalability and reproducibility that is more commonly associated with static languages. Typically, Julia users should be able to use the built-in package manager to manage their projects without needing a precise understanding of these interactions. A call to `Pkg.add("X")` will add to the appropriate project and manifest files, selected via `Pkg.activate("Y")`, so that a future call to `import X` will load `X` without further thought. +在软件包系统中,联邦软件包管理和精确的软件可复制性是困难但有价值的目标。结合起来,这些目标导致了一个比大多数动态语言更加复杂的包加载机制,但它也产生了通常与静态语言相关的可伸缩性和可复制性。通常,Julia用户应该能够使用内置的包管理器来管理他们的项目,而无需精确理解这些交互细节。通过调用`Pkg.add("X")`添加`X`包到对应的项目,并清晰显示相关文件,选择`Pkg.activate("Y")`后, 可调用`import X` 即可加载`X`包,而无需作过多考虑。 diff --git a/zh_CN/doc/src/manual/complex-and-rational-numbers.md b/zh_CN/doc/src/manual/complex-and-rational-numbers.md index b7bf5a00..19e9688e 100644 --- a/zh_CN/doc/src/manual/complex-and-rational-numbers.md +++ b/zh_CN/doc/src/manual/complex-and-rational-numbers.md @@ -4,7 +4,7 @@ Julia 语言包含了预定义的复数和有理数类型,并且支持它们 ## 复数 -在Julia中,全局常量 [`im`](@ref) 被绑定到复数 *i*,表示 -1 的主平方根。(不应使用数学家习惯的 `i` 或工程师习惯的 `j` 来表示此全局常量,因为它们是非常常用的索引变量名。)由于 Julia 允许数值字面量[作为系数与标识符并置](@ref man-numeric-literal-coefficients),这种绑定就足够为复数提供很方便的语法,类似于传统的数学记法: +在Julia中,全局常量 [`im`](@ref) 被绑定到复数 *i*,表示 -1 的主平方根(不应使用数学家习惯的 `i` 或工程师习惯的 `j` 来表示此全局常量,因为它们是非常常用的索引变量名)。由于 Julia 允许数值字面量[作为系数与标识符并置](@ref man-numeric-literal-coefficients),这种绑定就足够为复数提供很方便的语法,类似于传统的数学记法: ```jldoctest julia> 1+2im @@ -76,7 +76,7 @@ julia> 1 + 3/4im 1.0 - 0.75im ``` -注意 `3/4im == 3/(4*im) == -(3/4*im)`,因为文本系数比除法的优先级更高。 +注意 `3/4im == 3/(4*im) == -(3/4*im)`,因为系数比除法的优先级更高。 Julia 提供了一些操作复数的标准函数: @@ -103,7 +103,7 @@ julia> angle(1 + 2im) # 以弧度为单位的相位角 1.1071487177940904 ``` -按照惯例,复数的绝对值([`abs`](@ref))是从零点到它的距离。[`abs2`](@ref) 给出绝对值的平方,作用于复数上时非常有用,因为它避免了取平方根。[`angle`](@ref) 返回以弧度为单位的相位角(这也被称为辐角函数)。所有其它的[初等函数](@ref)在复数上也都有完整的定义: +按照惯例,复数的绝对值([`abs`](@ref))是从零点到它的距离。[`abs2`](@ref) 给出绝对值的平方,作用于复数上时非常有用,因为它避免了取平方根。[`angle`](@ref) 返回以弧度为单位的相位角(也被称为辐角函数)。所有其它的[初等函数](@ref)在复数上也都有完整的定义: ```jldoctest julia> sqrt(1im) diff --git a/zh_CN/doc/src/manual/constructors.md b/zh_CN/doc/src/manual/constructors.md index df6cd51b..314f3cb5 100644 --- a/zh_CN/doc/src/manual/constructors.md +++ b/zh_CN/doc/src/manual/constructors.md @@ -221,10 +221,10 @@ Point{Int64}(1, 2) julia> Point(1.0,2.5) ## 隐式的 T ## Point{Float64}(1.0, 2.5) -julia> Point(1,2.5) ## 隐式的 T ## +julia> Point(1,2.5) ## implicit T ## ERROR: MethodError: no method matching Point(::Int64, ::Float64) Closest candidates are: - Point(::T<:Real, ::T<:Real) where T<:Real at none:2 + Point(::T, ::T) where T<:Real at none:2 julia> Point{Int64}(1, 2) ## 显式的 T ## Point{Int64}(1, 2) @@ -279,7 +279,7 @@ Point{Float64} julia> Point(1.5,2) ERROR: MethodError: no method matching Point(::Float64, ::Int64) Closest candidates are: - Point(::T<:Real, !Matched::T<:Real) where T<:Real at none:1 + Point(::T, !Matched::T) where T<:Real at none:1 ``` 如果你想要找到一种方法可以使类似的调用都可以正常工作,请参阅[类型转换与类型提升](@ref conversion-and-promotion)。这里稍稍“剧透”一下,我们可以利用下面的这个外部构造函数来满足需求,无论输入参数的类型如何,它都可以触发通用的 `Point` 构造函数: diff --git a/zh_CN/doc/src/manual/control-flow.md b/zh_CN/doc/src/manual/control-flow.md index 9e95f4fd..4030e6dd 100644 --- a/zh_CN/doc/src/manual/control-flow.md +++ b/zh_CN/doc/src/manual/control-flow.md @@ -486,6 +486,7 @@ julia> for i = 1:2, j = 3:4 | [`ArgumentError`](@ref) | | [`BoundsError`](@ref) | | [`CompositeException`](@ref) | +| [`DimensionMismatch`](@ref) | | [`DivideError`](@ref) | | [`DomainError`](@ref) | | [`EOFError`](@ref) | @@ -629,22 +630,22 @@ Stacktrace: ### `try/catch` 语句 -The `try/catch` statement allows for `Exception`s to be tested for, and for the -graceful handling of things that may ordinarily break your application. For example, -in the below code the function for square root would normally throw an exception. By -placing a `try/catch` block around it we can mitigate that here. You may choose how -you wish to handle this exception, whether logging it, return a placeholder value or -as in the case below where we just printed out a statement. One thing to think about -when deciding how to handle unexpected situations is that using a `try/catch` block is -much slower than using conditional branching to handle those situations. -Below there are more examples of handling exceptions with a `try/catch` block: +通过 `try / catch` 语句,可以测试 Exception 并 +优雅处理可能会破坏应用程序的事情。 例如, +在下面的代码中,平方根函数会引发异常。 通过 +在其周围放置 `try / catch` 块可以缓解。 您可以选择如何 +处理此异常,无论是记录它,返回占位符值还是 +就像下面仅打印一句话。 要注意的是 +在决定如何处理异常时,使用`try / catch` 块 +比使用条件分支处理要慢得多。 +以下是使用` try / catch` 块处理异常的更多示例: ```jldoctest julia> try - sqrt("ten") - catch e - println("You should have entered a numeric value") - end +sqrt("ten") +catch e +println("You should have entered a numeric value") +end You should have entered a numeric value ``` @@ -821,6 +822,6 @@ taskHdl = @task mytask(7) | 符号 | 含义 | |:----------- |:-------------------------------------------------- | -| `:runnable` | Currently running, or able to run | +| `:runnable` | 正在运行,或者可以被切换到 | | `:done` | 成功结束执行 | | `:failed` | 以一个没被捕获的异常结束 | diff --git a/zh_CN/doc/src/manual/documentation.md b/zh_CN/doc/src/manual/documentation.md index ec80e408..0089a6d3 100644 --- a/zh_CN/doc/src/manual/documentation.md +++ b/zh_CN/doc/src/manual/documentation.md @@ -251,7 +251,8 @@ search: * .* ## 进阶用法 -`@doc`宏在叫做`META`的每个模块的字典中连接他的第一个和第二个参数。默认地,文档希望是用Markdown写成,并且`doc"""`字符串宏简单地创造一个代表了Markdown内容的对象。在未来,有可能支持更多的进阶功能比如考虑到相关的图像或者链接路径。 +The `@doc` macro associates its first argument with its second in a per-module dictionary called +`META`. 为了让写文档更加简单,语法分析器对宏名`@doc`特殊对待:如果`@doc`的调用只有一个参数,但是在下一行出现了另外一个表达式,那么这个表达式就会追加为宏的参数。所以接下来的语法会被分析成`@doc`的2个参数的调用: @@ -262,7 +263,7 @@ search: * .* f(x) = x ``` -这就让使用任意对象(这里指的是`原始`字符串)作为docstring变得简单。 +This makes it possible to use expressions other than normal string literals (such as the `raw""` string macro) as a docstring. 当`@doc`宏(或者`doc`函数)用作抽取文档时,他会在所有的`META`字典寻找与对象相关的元数据并且返回。返回的对象(例如一些Markdown内容)会默认智能地显示。这个设计也让以编程方法使用文档系统变得容易;例如,在一个函数的不同版本中重用文档: @@ -313,11 +314,24 @@ y = MyType("y") ## 语法指南 -对于所有的可写文档的Julia语法的总览。 +A comprehensive overview of all documentable Julia syntax. +In the following examples `"..."` is used to illustrate an arbitrary docstring. -在下述例子中`"..."`用来表示任意的docstring。 +### `$` and `\` characters -`doc""`只应用在如下情况下:docstring包含不应被Julia分析的`$`或者`\`字符,比如LaTeX语法;Julia源码中包含插值。 +The `$` and `\` characters are still parsed as string interpolation or start of an escape sequence +in docstrings too. The `raw""` string macro together with the `@doc` macro can be used to avoid +having to escape them. This is handy when the docstrings include LaTeX or Julia source code examples +containing interpolation: + +````julia +@doc raw""" +```math +\LaTeX +``` +""" +function f end +```` ### 函数与方法 diff --git a/zh_CN/doc/src/manual/embedding.md b/zh_CN/doc/src/manual/embedding.md index 544a3b16..9c600cd4 100644 --- a/zh_CN/doc/src/manual/embedding.md +++ b/zh_CN/doc/src/manual/embedding.md @@ -119,7 +119,6 @@ We start by opening Visual Studio and creating a new Console Application project header file, add the following lines at the end: ```c -#define JULIA_ENABLE_THREADING #include ``` diff --git a/zh_CN/doc/src/manual/environment-variables.md b/zh_CN/doc/src/manual/environment-variables.md index 2ca697ba..54d6f7da 100644 --- a/zh_CN/doc/src/manual/environment-variables.md +++ b/zh_CN/doc/src/manual/environment-variables.md @@ -6,7 +6,11 @@ Julia 使用的环境变量通常以 `JULIA` 开头。如果调用 [`Interactive !!! note - 某些变量需要在 Julia 启动之前设置,比如 `JULIA_NUM_THREADS` 和 `JULIA_PROJECT`,因为在启动过程中将这些变量添加到 `~/.julia/config/startup.jl` 中为时已晚。在 Bash 中,环境变量可以手动设置,这可通过在 Julia 启动前运行诸如 `export JULIA_NUM_THREADS=4` 的命令,亦可通过向 `-/.bashrc` 或 `~/.bash_profile` 添加相同命令来在 Bash 每次启动时设置该变量。 + Some variables, such as `JULIA_NUM_THREADS` and `JULIA_PROJECT`, need to be set before Julia + starts, therefore adding these to `~/.julia/config/startup.jl` is too late in the startup process. + In Bash, environment variables can either be set manually by running, e.g., + `export JULIA_NUM_THREADS=4` before starting Julia, or by adding the same command to + `~/.bashrc` or `~/.bash_profile` to set the variable each time Bash is started. ## 文件位置 @@ -95,8 +99,8 @@ environment variable or if it must have a value, set it to the string `:`. The `JULIA_DEPOT_PATH` environment variable is used to populate the global Julia [`DEPOT_PATH`](@ref) variable, which controls where the package manager, as well as Julia's code loading mechanisms, look for package registries, installed -packages, named environments, repo clones, cached compiled package images, and -configuration files. +packages, named environments, repo clones, cached compiled package images, +configuration files, and the default location of the REPL's history file. Unlike the shell `PATH` variable but similar to `JULIA_LOAD_PATH`, empty entries in `JULIA_DEPOT_PATH` are expanded to the default value of `DEPOT_PATH`. This allows @@ -121,7 +125,7 @@ or if it must have a value, set it to the string `:`. REPL 历史文件中 `REPL.find_hist_file()` 的绝对路径。如果没有设置 `$JULIA_HISTORY`,那么 `REPL.find_hist_file()` 默认为 ``` -$HOME/.julia/logs/repl_history.jl +$(DEPOT_PATH[1])/logs/repl_history.jl ``` ### `JULIA_PKGRESOLVE_ACCURACY` @@ -262,7 +266,4 @@ Julia 用来执行外部命令的 shell 的绝对路径(通过 `Base.repl_cmd( 传递给 LLVM 后端的参数。 -### `JULIA_DEBUG_LOADING` - -如果设置,那么 Julia 会打印在 [`Base.require`](@ref) 加载过程中的有关缓存的详细信息。 diff --git a/zh_CN/doc/src/manual/faq.md b/zh_CN/doc/src/manual/faq.md index d9563ae2..aae2088b 100644 --- a/zh_CN/doc/src/manual/faq.md +++ b/zh_CN/doc/src/manual/faq.md @@ -1,5 +1,23 @@ # 常见问题 +## General + +### Is Julia named after someone or something? + +No. + +### Why don't you compile Matlab/Python/R/… code to Julia? + +Since many people are familiar with the syntax of other dynamic languages, and lots of code has already been written in those languages, it is natural to wonder why we didn't just plug a Matlab or Python front-end into a Julia back-end (or “transpile” code to Julia) in order to get all the performance benefits of Julia without requiring programmers to learn a new language. Simple, right? + +The basic issue is that there is *nothing special about Julia's compiler*: we use a commonplace compiler (LLVM) with no “secret sauce” that other language developers don't know about. Indeed, Julia's compiler is in many ways much simpler than those of other dynamic languages (e.g. PyPy or LuaJIT). Julia's performance advantage derives almost entirely from its front-end: its language semantics allow a [well-written Julia program](@ref man-performance-tips) to *give more opportunities to the compiler* to generate efficient code and memory layouts. If you tried to compile Matlab or Python code to Julia, our compiler would be limited by the semantics of Matlab or Python to producing code no better than that of existing compilers for those languages (and probably worse). The key role of semantics is also why several existing Python compilers (like Numba and Pythran) only attempt to optimize a small subset of the language (e.g. operations on Numpy arrays and scalars), and for this subset they are already doing at least as well as we could for the same semantics. The people working on those projects are incredibly smart and have accomplished amazing things, but retrofitting a compiler onto a language that was designed to be interpreted is a very difficult problem. + +Julia's advantage is that good performance is not limited to a small subset of “built-in” types and operations, and one can write high-level type-generic code that works on arbitrary user-defined types while remaining fast and memory-efficient. Types in languages like Python simply don't provide enough information to the compiler for similar capabilities, so as soon as you used those languages as a Julia front-end you would be stuck. + +For similar reasons, automated translation to Julia would also typically generate unreadable, slow, non-idiomatic code that would not be a good starting point for a native Julia port from another language. + +On the other hand, language *interoperability* is extremely useful: we want to exploit existing high-quality code in other languages from Julia (and vice versa)! The best way to enable this is not a transpiler, but rather via easy inter-language calling facilities. We have worked hard on this, from the built-in `ccall` intrinsic (to call C and Fortran libraries) to [JuliaInterop](https://github.com/JuliaInterop) packages that connect Julia to Python, Matlab, C++, and more. + ## 会话和 REPL ### 如何从内存中删除某个对象? @@ -285,7 +303,7 @@ julia> sqrt(-2.0+0im) 0.0 + 1.4142135623730951im ``` -### 为什么Julia使用原生机器整数算法? +### [Why does Julia use native machine integer arithmetic?](@id faq-integer-arithmetic) Julia使用机器算法进行整数计算。这意味着`Int`的范围是有界的,值在范围的两端循环,也就是说整数的加法,减法和乘法会出现上溢或者下溢,导致出现某些从开始就令人不安的结果: @@ -655,8 +673,8 @@ julia> A = zeros() 它的长度为`1`。 * 零维数组原生没有任何你可以索引的维度 -- 它们仅仅是`A[]`。我们可以给它们应用同样的"trailing one"规则, - 如同所有其他的数组维度一样,所以你实际上可以使用 - `A[1]`,`A[1,1]`等来索引 + as for all other array dimensionalities, so you can indeed index them as `A[1]`, `A[1,1]`, etc; see + [Omitted and extra indices](@ref). 理解它与普通的标量之间的区别也很重要。标量不是一个可变的容器(尽管它们是可迭代的,可以定义像`length`,`getindex`这样的东西,*例如*`1[] == 1`)。特别地,如果`x = 0.0`是以一个标量来定义,尝试通过`x[] = 1.0`来改变它的值会报错。标量`x`能够通过`fill(x)`转化成包含它的零维数组,并且相对地,一个零维数组`a`可以通过`a[]`转化成其包含的标量。另外一个区别是标量可以参与到线性代数运算中,比如`2 * rand(2,2)`,但是零维数组的相似操作`fill(2) * rand(2,2)`会报错。 @@ -686,18 +704,29 @@ Modifying OpenBLAS settings or compiling Julia with a different BLAS library, eg ## Julia 版本发布 -### 应该使用 Julia 的正式版(release version),测试版(beta version)还是每夜更新版(nightly version)? +### Do I want to use the Stable, LTS, or nightly version of Julia? -如果您正在寻找稳定的代码库,您可能更喜欢Julia的正式版。 通常每6个月发布一次,为您提供编写代码的稳定平台。 +The Stable version of Julia is the latest released version of Julia, this is the version most people will want to run. +It has the latest features, including improved performance. +The Stable version of Julia is versioned according to [SemVer](https://semver.org/) as v1.x.y. +A new minor release of Julia corresponding to a new Stable version is made approximately every 4-5 months after a few weeks of testing as a release candidate. +Unlike the LTS version the a Stable version will not normally recieve bugfixes after another Stable version of Julia has been released. +However, upgrading to the next Stable release will always be possible as each release of Julia v1.x will continue to run code written for earlier versions. -如果您不介意稍微落后于最新的错误修正和更改,觉得稍微更快的修改更具吸引力,您可能更喜欢Julia的测试版。 此外,这些二进制文件在发布之前会进行测试,以确保它们完全正常运行。 +You may prefer the LTS (Long Term Support) version of Julia if you are looking for a very stable code base. +The current LTS version of Julia is versioned according to SemVer as v1.0.x; +this branch will continue to recieve bugfixes until a new LTS branch is chosen, at which point the v1.0.x series will no longer recieved regular bug fixes and all but the most conservative users will be advised to upgrade to the new LTS version series. +As a package developer, you may prefer to develop for the LTS version, to maximize the number of users who can use your package. +As per SemVer, code written for v1.0 will continue to work for all future LTS and Stable versions. +In general, even if targetting the LTS, one can develop and run code in the latest Stable version, to take advantage of the improved performance; so long as one avoids using new features (such as added library functions or new methods). -如果您想利用该语言的最新更新,并且不介意今天可用的版本偶尔出现实际上并没有正常工作,您可能更喜欢 Julia 的每夜更新版。 +You may prefer the nightly version of Julia if you want to take advantage of the latest updates to the language, and don't mind if the version available today occasionally doesn't actually work. +As the name implies, releases to the nightly version are made roughly every night (depending on build infrastructure stability). +In general nightly released are fairly safe to use—your code will not catch on fire. +However, they may be occasional regressions and or issues that will not be found until more thorough pre-release testing. +You may wish to test against the nightly version to ensure that such regressions that affect your use case are caught before a release is made. -最后,您也可以考虑自己从源代码编译Julia。 此选项主要适用于那些适应命令行或对学习感兴趣的人。 如果您是这样,您可能也有兴趣阅读我们的[贡献指南](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md)。 +Finally, you may also consider building Julia from source for yourself. This option is mainly for those individuals who are comfortable at the command line, or interested in learning. +If this describes you, you may also be interested in reading our [guidelines for contributing](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md). 可以在[https://julialang.org/downloads/](https://julialang.org/downloads/)的下载页面上找到每种下载类型的链接。 请注意,并非所有版本的Julia都适用于所有平台。 - -### 已弃用的功能会在何时移除? - -已弃用的函数会被随后发布的版本中移除。例如,在1.0版本中被标记为已弃用的函数会在0.2版本及之后的版本中无法使用。 diff --git a/zh_CN/doc/src/manual/functions.md b/zh_CN/doc/src/manual/functions.md index b7abcf89..473851c7 100644 --- a/zh_CN/doc/src/manual/functions.md +++ b/zh_CN/doc/src/manual/functions.md @@ -193,6 +193,24 @@ julia> map(x -> x^2 + 2x - 1, [1,3,-1]) 接受多个参数的匿名函数写法可以使用语法 `(x,y,z)->2x+y-z`,而无参匿名函数写作 `()->3` 。无参函数的这种写法看起来可能有些奇怪,不过它对于延迟计算很有必要。这种用法会把代码块包进一个无参函数中,后续把它当做 `f` 调用。 +As an example, consider this call to [`get`](@ref): + +```julia +get(dict, key) do + # default value calculated here + time() +end +``` + +上面的代码等效于使用包含代码的匿名函数调用`get`。 被包围在do和end之间,如下所示 + +```julia +get(()->time(), dict, key) +``` + +The call to [`time`](@ref) is delayed by wrapping it in a 0-argument anonymous function +that is called only when the requested key is absent from `dict`. + ## 元组 Julia 有一个和函数参数与返回值密切相关的内置数据结构叫做元组(*tuple*)。 @@ -563,7 +581,7 @@ julia> (sqrt ∘ +)(3, 6) 3.0 ``` -This adds the numbers first, then finds the square root of the result. +这个语句先把数字相加,再对结果求平方根。 The next example composes three functions and maps the result over an array of strings: diff --git a/zh_CN/doc/src/manual/getting-started.md b/zh_CN/doc/src/manual/getting-started.md index f6082d50..547b4533 100644 --- a/zh_CN/doc/src/manual/getting-started.md +++ b/zh_CN/doc/src/manual/getting-started.md @@ -78,7 +78,7 @@ julia [switches] -- [programfile] [args...] |选项 |描述| |:--- |:---| |`-v`, `--version` |显示版本信息| -|`-h`, `--help` |打印本条帮助信息| +|`-h`, `--help` |Print command-line options (this message).| |`--project[={\|@.}]` |将 设置为主项目/环境。默认的 @. 选项将搜索父目录,直至找到 Project.toml 或 JuliaProject.toml 文件。| |`-J`, `--sysimage ` |用指定的镜像文件(system image file)启动| |`-H`, `--home ` |设置 `julia` 可执行文件的路径| diff --git a/zh_CN/doc/src/manual/mathematical-operations.md b/zh_CN/doc/src/manual/mathematical-operations.md index 8c1583d3..fbfbc57e 100644 --- a/zh_CN/doc/src/manual/mathematical-operations.md +++ b/zh_CN/doc/src/manual/mathematical-operations.md @@ -25,6 +25,8 @@ Julia 为它所有的基础数值类型,提供了整套的基础算术和位 |:---------- |:-------- |:---------------------------------------- | | `!x` | 否定 | 将 `true` 和 `false` 互换 | +除了优先级比二元操作符高以外,直接放在标识符或括号前的数字,如 `2x` 或 `2(x+y)` 还会被视为乘法。详见[数值字面量系数](@ref man-numeric-literal-coefficients)。 + Julia 的类型提升系统使得混合参数类型上的代数运算也能顺其自然的工作,请参考[类型提升系统](@ref conversion-and-promotion)来了解更多内容。 这里是使用算术运算符的一些简单例子: @@ -210,7 +212,7 @@ julia> NaN > NaN false ``` -由于 `NaN` 的存在,在做[数组](@ref man-multi-dim-arrays)比较时会特别头疼: +当你将 `NaN` 和 [数组](@ref man-multi-dim-arrays) 一起连用时,你就会感到头疼: ```jldoctest julia> [1 NaN] == [1 NaN] @@ -320,6 +322,8 @@ Julia 提供了强大的数学函数和运算符集合。这些数学运算定 要看**全部** Julia 运算符的优先级关系,可以看这个文件的最上面部分:[`src/julia-parser.scm`](https://github.com/JuliaLang/julia/blob/master/src/julia-parser.scm) +[数字字面量系数](@ref man-numeric-literal-coefficients),例如 `2x` 中的 2,它的优先级比二元运算符高,因此会当作乘法,并且它的优先级也比 `^` 高。 + 你也可以通过内置函数 `Base.operator_precedence` 查看任何给定运算符的优先级数值,数值越大优先级越高: ```jldoctest @@ -350,8 +354,8 @@ Julia 支持三种数值转换,它们在处理不精确转换上有所不同 * 如果 `T` 是浮点类型,转换的结果就是最近的可表示值, 可能会是正负无穷大。 - * 如果 `T` 为整数类型,当 `x` 不为 `T` 类型时,会触发 `InexactError` - * `x % T` 将整数 `x` 转换为整型 `T`,与 `x` 模 `2^n` 的结果一致,其中 `n` 是 `T` 的位数。换句话说,如果用二进制表示是被砍掉一部分的。 + * 如果 `T` 为整数类型,当 `x` 不能由 `T` 类型表示时,会抛出 `InexactError`。 + * `x % T` 将整数 `x` 转换为整型 `T`,与 `x` 模 `2^n` 的结果一致,其中 `n` 是 `T` 的位数。换句话说,在二进制表示下被截掉了一部分。 * [舍入函数](@ref) 接收一个 `T` 类型的可选参数。比如,`round(Int,x)` @@ -404,8 +408,8 @@ Stacktrace: |:--------------------- |:-------------------------------- |:----------- | | [`round(x)`](@ref) | `x` 舍到最接近的整数 | `typeof(x)` | | [`round(T, x)`](@ref) | `x` 舍到最接近的整数 | `T` | -| [`floor(x)`](@ref) | `x` 舍到`-Inf` | `typeof(x)` | -| [`floor(T, x)`](@ref) | `x` 舍到`-Inf` | `T` | +| [`floor(x)`](@ref) | `x` 向 `-Inf` 舍入 | `typeof(x)` | +| [`floor(T, x)`](@ref) | `x` 向 `-Inf` 舍入 | `T` | | [`ceil(x)`](@ref) | `x` 向 `+Inf` 方向取整 | `typeof(x)` | | [`ceil(T, x)`](@ref) | `x` 向 `+Inf` 方向取整 | `T` | | [`trunc(x)`](@ref) | `x` 向 0 取整 | `typeof(x)` | diff --git a/zh_CN/doc/src/manual/metaprogramming.md b/zh_CN/doc/src/manual/metaprogramming.md index 7c16ae19..da93f5ec 100644 --- a/zh_CN/doc/src/manual/metaprogramming.md +++ b/zh_CN/doc/src/manual/metaprogramming.md @@ -112,6 +112,9 @@ julia> Symbol(:var,'_',"sym") :var_sym ``` +Note that to use `:` syntax, the symbol's name must be a valid identifier. +Otherwise the `Symbol(str)` constructor must be used. + 在表达式的上下文中,符号用来表示对变量的访问;当一个表达式被求值时,符号会被替换为这个符号在合适的 [scope](@ref scope-of-variables) 中所绑定的值。 Sometimes extra parentheses around the argument to `:` are needed to avoid ambiguity in parsing: @@ -269,7 +272,7 @@ end 这种行为背后的直觉是每个 `$` 都将 `x` 求值一遍:一个 `$` 工作方式类似于 `eval(:x)`,其返回 `x` 的值,而两个 `$` 行为相当于 `eval(eval(:x))`。 -### QuoteNode +### [QuoteNode](@id man-quote-node) The usual representation of a `quote` form in an AST is an [`Expr`](@ref) with head `:quote`: @@ -286,19 +289,30 @@ Expr 3: Int64 2 ``` -正如我们所看到的,这种表达式可以使用 `$` 进行插值。但是,在某些情况下,必须引用代码而*不*执行插值。这种引用还没有语法,但它在内部表示为 `QuoteNode` 类型的对象。对于像符号这样的简单引用项,解析器生成 `QuoteNode`: +As we have seen, such expressions support interpolation with `$`. +However, in some situations it is necessary to quote code *without* performing interpolation. +This kind of quoting does not yet have syntax, but is represented internally +as an object of type `QuoteNode`: +```jldoctest interp1 +julia> eval(Meta.quot(Expr(:$, :(1+2)))) +3 +julia> eval(QuoteNode(Expr(:$, :(1+2)))) +:($(Expr(:$, :(1 + 2)))) +``` +The parser yields `QuoteNode`s for simple quoted items like symbols: ```jldoctest interp1 julia> dump(Meta.parse(":x")) QuoteNode value: Symbol x ``` -`QuoteNode` 也可用于某些高级的元编程任务。 +`QuoteNode` can also be used for certain advanced metaprogramming tasks. -### [`eval`](@ref) 及其效果 +### Evaluating expressions -给定一个表达式对象,可以通过 [`eval`](@ref) 使 Julia 在全局作用域内求值(执行)它: +Given an expression object, one can cause Julia to evaluate (execute) it at global scope using +[`eval`](@ref): ```jldoctest interp1 julia> :(1 + 2) @@ -320,7 +334,9 @@ julia> eval(ex) 3 ``` -每个[模块](@ref modules)有自己的 [`eval`](@ref) 函数,该函数在其全局作用域内对表达式求值。传给 [`eval`](@ref) 的表达式不止可以返回值——它们还能具有改变封闭模块的环境状态的副作用: +Every [module](@ref modules) has its own [`eval`](@ref) function that evaluates expressions in its global +scope. Expressions passed to [`eval`](@ref) are not limited to returning values -- they can +also have side-effects that alter the state of the enclosing module's environment: ```jldoctest julia> ex = :(x = 1) @@ -922,16 +938,17 @@ end 生成函数的声明不会执行某些计算或操作,而会返回一个被引用的表达式,接着该表达式构成参数类型所对应方法的主体。在调用生成函数时,其返回的表达式会被编译然后执行。为了提高效率,通常会缓存结果。为了能推断是否缓存结果,只能使用语言的受限子集。因此,生成函数提供了一个灵活的方式来将工作重运行时移到编译时,代价则是其构造能力受到更大的限制。 -定义生成函数与普通函数有四个主要区别: +When defining generated functions, there are five main differences to ordinary functions: 1. 使用 `@generated` 标注函数声明。这会向 AST 附加一些信息,让编译器知道这个函数是生成函数。 2. 在生成函数的主体中,你只能访问参数的*类型*,而不能访问其值,以及在生成函数的定义之前便已定义的任何函数。 - - + not their values. 3. 不应计算某些东西或执行某些操作,应返回一个*被引用的*表达式,它会在被求值时执行你想要的操作。 -4. 生成函数不能*更改*或*观察*任何非常量的全局状态。(例如,其包括 IO、锁、非局部的字典或者使用 `hasmethod`)即它们只能读取全局常量,且没有任何副作用。换句话说,它们必须是纯函数。由于实现限制,这也意味着它们目前无法定义闭包或生成器。 +4. Generated functions are only permitted to call functions that were defined *before* the definition of the generated + function. (Failure to follow this my result on getting `MethodErrors` referring to functions from a future world-age.) +5. 生成函数不能*更改*或*观察*任何非常量的全局状态。(例如,其包括 IO、锁、非局部的字典或者使用 `hasmethod`)即它们只能读取全局常量,且没有任何副作用。换句话说,它们必须是纯函数。由于实现限制,这也意味着它们目前无法定义闭包或生成器。 for example, IO, locks, non-local dictionaries, or using [`hasmethod`](@ref)). diff --git a/zh_CN/doc/src/manual/methods.md b/zh_CN/doc/src/manual/methods.md index b123eaa7..b4b1f75c 100644 --- a/zh_CN/doc/src/manual/methods.md +++ b/zh_CN/doc/src/manual/methods.md @@ -174,8 +174,8 @@ julia> g(2, 3.0) julia> g(2.0, 3.0) ERROR: MethodError: g(::Float64, ::Float64) is ambiguous. Candidates: - g(x, y::Float64) in Main at none:1 g(x::Float64, y) in Main at none:1 + g(x, y::Float64) in Main at none:1 Possible fix, define g(::Float64, ::Float64) ``` @@ -301,7 +301,7 @@ true julia> same_type_numeric("foo", 2.0) ERROR: MethodError: no method matching same_type_numeric(::String, ::Float64) Closest candidates are: - same_type_numeric(!Matched::T<:Number, ::T<:Number) where T<:Number at none:1 + same_type_numeric(!Matched::T, ::T) where T<:Number at none:1 same_type_numeric(!Matched::Number, ::Number) at none:1 julia> same_type_numeric("foo", "bar") @@ -422,7 +422,9 @@ julia> fetch(schedule(t, 1)) abstract type AbstractArray{T, N} end eltype(::Type{<:AbstractArray{T}}) where {T} = T ``` -使用了所谓的三角分派。注意如果 `T` 是一个 `UnionAll` 类型,比如 `eltype(Array{T} where T <: Integer)`,会返回 `Any`(如同 `Base` 中的 `eltype` 一样)。 +using so-called triangular dispatch. Note that if `T` is a `UnionAll` +type, as e.g. `eltype(Array{T} where T <: Integer)`, then `Any` is +returned (as does the version of `eltype` in `Base`). 另外一个方法,这是在Julia v0.6中的三角分派到来之前的唯一正确方法,是: @@ -765,7 +767,10 @@ f(x::T, y::T) where {T} = ... f(x, y) = f(promote(x, y)...) ``` -这个设计的一个隐患是如果没有合适的把`x`和`y`转换到同样类型的类型提升方法,第二个方法就可能无限自递归然后引发堆溢出。非输出函数`Base.promote_noncircular`可以用作一个替代方案;当类型提升失败它依旧会扔出一个错误,但是有更加特定的错误信息时会失败更快。 +One risk with this design is the possibility that if there is no +suitable promotion method converting `x` and `y` to the same type, the +second method will recurse on itself infinitely and trigger a stack +overflow. ### 一次只根据一个参数分派 diff --git a/zh_CN/doc/src/manual/modules.md b/zh_CN/doc/src/manual/modules.md index da6b06c7..34c181e9 100644 --- a/zh_CN/doc/src/manual/modules.md +++ b/zh_CN/doc/src/manual/modules.md @@ -33,12 +33,11 @@ end 代码 `using BigLib: thing1, thing2` 显式地将标识符 `thing1` 和 `thing2` 从模块 `BigLib` 中引入到当前作用域。如果这两个变量是函数的话,则**不允许**给它们增加新的方法,毕竟代码里写的是 "using"(使用)它们,而不是扩展它们。 -The [`import`](@ref) keyword supports the same syntax as [`using`](@ref), but only operates on a single name -at a time. It does not add modules to be searched the way `using` does. `import` also differs -from `using` in that functions imported using `import` can be extended with new methods. +[`import`](@ref) 关键字所支持的语法与 [`using`](@ref) 一致。 +它并不会像 `using` 那样将模块添加到搜索空间中。 +与 `using` 不同,`import` 引入的函数 **可以** 为其增加新的方法。 -In `MyModule` above we wanted to add a method to the standard [`show`](@ref) function, so we had to write -`import Base.show`. Functions whose names are only visible via `using` cannot be extended. +前面的 `MyModule` 模块中,我们希望给 [`show`](@ref) 函数增加一个方法,需要写成 `import Base.show`。如果用 `using` 的话,就不能扩展 `show` 函数。通过 `using` 导入才可见的名字是不能被扩展的。 一旦一个变量通过 `using` 或 `import` 引入,当前模块就不能创建同名的变量了。而且导入的变量是只读的,给全局变量赋值只能影响到由当前模块拥有的变量,否则会报错。 @@ -60,13 +59,13 @@ end 这个模块用关键字 `export` 导出了 `x` 和 `y` 函数,此外还有一个没有被导出的函数 `p`。想要将该模块及其内部的函数导入当前模块有以下方法: -| `import` 命令 | 将哪些变量导入了当前作用域? | 如何添加或扩展方法 | +| 导入代码 | 当前作用域导入了哪些变量? | 可增加新方法的名字 | |:------------------------------- |:------------------------------------------------------------------------------- |:------------------------------------------- | | `using MyModule` | All `export`ed names (`x` and `y`), `MyModule.x`, `MyModule.y` and `MyModule.p` | `MyModule.x`, `MyModule.y` and `MyModule.p` | | `using MyModule: x, p` | `x` and `p` |   | -| `import MyModule` | `MyModule.x`, `MyModule.y` and `MyModule.p` | `MyModule.x`, `MyModule.y` and `MyModule.p` | -| `import MyModule.x, MyModule.p` | `x` and `p` | `x` and `p` | -| `import MyModule: x, p` | `x` and `p` | `x` and `p` | +| `import MyModule` | `MyModule.x`、`MyModule.y` 和 `MyModule.p` | `MyModule.x`、`MyModule.y` 和 `MyModule.p` | +| `import MyModule.x, MyModule.p` | `x` 和 `p` | `x` 和 `p` | +| `import MyModule: x, p` | `x` 和 `p` | `x` 和 `p` | ### 模块和文件 @@ -96,20 +95,16 @@ end ### 标准模块 -There are three important standard modules: -* [`Core`](@ref) contains all functionality "built into" the language. -* [`Base`](@ref) contains basic functionality that is useful in almost all cases. -* [`Main`](@ref) is the top-level module and the current module, when Julia is started. +有三个重要的标准模块: +* [`Core`](@ref) 包含了语言“内置”的所有功能。 +* [`Base`](@ref) 包含了绝大多数情况下都会用到的基本功能。 +* [`Main`](@ref) 是顶层模块,当 julia 启动时,也是当前模块。 ### 默认顶层定义以及裸模块 -In addition to `using Base`, modules also automatically contain -definitions of the [`eval`](@ref) and [`include`](@ref) functions, -which evaluate expressions/files within the global scope of that module. +除了默认包含 `using Base` 之外,所有模块都还包含 [`eval`](@ref) 和 [`include`](@ref) 函数。这两个函数用于在对应模块的全局环境中,执行表达式或文件。 -If these default definitions are not wanted, modules can be defined using the keyword [`baremodule`](@ref) -instead (note: `Core` is still imported, as per above). In terms of `baremodule`, a standard -`module` looks like this: +如果连这些默认的定义都不需要,那么可以用 [`baremodule`](@ref) 定义裸模块(不过 `Core` 模块仍然会被引入,否则啥也干不了)。用裸模块表达的标准模块定义如下: ``` baremodule Mod @@ -161,13 +156,10 @@ end 因为执行模块中的所有语句通常需要编译大量代码,大型模块可能需要几秒钟才能加载。Julia 会创建模块的预编译缓存以减少这个时间。 -The incremental precompiled module file are created and used automatically when using `import` -or `using` to load a module. This will cause it to be automatically compiled the first time -it is imported. Alternatively, you can manually call [`Base.compilecache(modulename)`](@ref). The resulting -cache files will be stored in `DEPOT_PATH[1]/compiled/`. Subsequently, the module is automatically -recompiled upon `using` or `import` whenever any of its dependencies change; dependencies are modules it -imports, the Julia build, files it includes, or explicit dependencies declared by [`include_dependency(path)`](@ref) -in the module file(s). +当用 `import` 或 `using` 加载一个模块时,模块增量预编译文件会自动创建并使用。这会让模块在第一次加载时自动编译。 +另外,你也可以手工调用 [`Base.compilecache(modulename)`](@ref),产生的缓存文件会放在 `DEPOT_PATH[1]/compiled/` 目录下。 +之后,当该模块的任何一个依赖发生变更时,该模块会在 `using` 或 `import` 时自动重新编译; +模块的依赖指的是:任何它导入的模块、Julia 自身、include 的文件或由 [`include_dependency(path)`](@ref) 显式声明的依赖。 对于文件依赖,判断是否有变动的方法是:在 `include` 或 `include_dependency` 的时候检查每个文件的变更时间(`mtime`)是否没变,或等于截断变更时间。截断变更时间是指将变更时间截断到最近的一秒,这是由于在某些操作系统中,用 `mtime` 无法获取亚秒级的精度。此外,也会考虑到 `require` 搜索到的文件路径与之前预编译文件中的是否匹配。对于已经加载到当前进程的依赖,即使它们的文件发成了变更,甚至是丢失,Julia 也不会重新编译这些模块,这是为了避免正在运行的系统与预编译缓存之间的不兼容性。 @@ -190,29 +182,19 @@ end 注意,在像 `__init__` 这样的函数里定义一个全局变量是完全可以的,这是动态语言的优点之一。但是把全局作用域的值定义成常量,可以让编译器能确定该值的类型,并且能让编译器生成更好的优化过的代码。显然,你的模块(Module)中,任何其他依赖于 `foo_data_ptr` 的全局量也必须在 `__init__` 中被初始化。 -Constants involving most Julia objects that are not produced by [`ccall`](@ref) do not need to be placed -in `__init__`: their definitions can be precompiled and loaded from the cached module image. This -includes complicated heap-allocated objects like arrays. However, any routine that returns a raw -pointer value must be called at runtime for precompilation to work ([`Ptr`](@ref) objects will turn into -null pointers unless they are hidden inside an [`isbits`](@ref) object). This includes the return values -of the Julia functions `cfunction` and [`pointer`](@ref). - -Dictionary and set types, or in general anything that depends on the output of a `hash(key)` method, -are a trickier case. In the common case where the keys are numbers, strings, symbols, ranges, -`Expr`, or compositions of these types (via arrays, tuples, sets, pairs, etc.) they are safe to -precompile. However, for a few other key types, such as `Function` or `DataType` and generic -user-defined types where you haven't defined a `hash` method, the fallback `hash` method depends -on the memory address of the object (via its `objectid`) and hence may change from run to run. -If you have one of these key types, or if you aren't sure, to be safe you can initialize this -dictionary from within your `__init__` function. Alternatively, you can use the [`IdDict`](@ref) -dictionary type, which is specially handled by precompilation so that it is safe to initialize -at compile-time. +涉及大多数不是由 [`ccall`](@ref) 生成的 Julia 对象的常量,不需要放在 `__init__` 中:它们的定义可以预编译并从缓存的模块映像中加载。这包括复杂的堆分配对象,如数组。 +但是,任何返回原始指针值的例程都必须在运行时调用,以便进行预编译(除非将 [`Ptr`](@ref) 对象隐藏在 [`isbits`](@ref) 对象中,否则它们将转换为空指针)。 +这包括 Julia 函数 `cfunction` 和 [`pointer`](@ref) 的返回值。 + +字典和集合类型,或者通常任何依赖于 `hash(key)` 方法的类型,都是比较棘手的情况。 +通常当键是数字、字符串、符号、范围、`Expr` 或这些类型的组合(通过数组、元组、集合、映射对等)时,可以安全地预编译它们。但是,对于一些其它的键类型,例如 `Function` 或 `DataType`、以及还没有定义散列方法的通用用户定义类型,回退(fallback)的散列(`hash`)方法依赖于对象的内存地址(通过 `objectid`),因此可能会在每次运行时发生变化。 +如果您有这些关键类型中的一种,或者您不确定,为了安全起见,您可以在您的 `__init__` 函数中初始化这个字典。或者,您可以使用 [`IdDict`](@ref) 字典类型,它是由预编译专门处理的,因此在编译时初始化是安全的。 当使用预编译时,我们必须要清楚地区分代码的编译阶段和运行阶段。在此模式下,我们会更清楚发现 Julia 的编译器可以执行任何 Julia 代码,而不是一个用于生成编译后代码的独立的解释器。 其它已知的潜在失败场景包括: -1. Global counters (for example, for attempting to uniquely identify objects). Consider the following +1. 全局计数器,例如:为了试图唯一的标识对象。考虑以下代码片段: ```julia @@ -228,7 +210,7 @@ at compile-time. - 注意 `objectid` (工作原理是 hash 内存指针)也有类似的问题,请查阅下面关于 `Dict` 的用法。 + 注意 `objectid` (工作原理是取内存指针的 hash)也有类似的问题,请查阅下面关于 `Dict` 的用法。 一种解决方案是用宏捕捉 [`@__MODULE__`](@ref),并将它与目前的 `counter` 值一起保存。然而,更好的方案是对代码进行重新设计,不要依赖这种全局状态变量。 @@ -255,12 +237,12 @@ at compile-time. 2. 当 `__init__()` 已经开始执行后,在局部作用域中声明 `global const`(见 issue #12010,计划为此情况添加一个错误提示) -3. 在增量预编译时替换模块是一个运行期间的错误。 +3. 在增量预编译时替换模块是一个运行时错误。 一些其他需要注意的点: -1. 在源代码文件本身被修改之后,不会执行代码重载或缓存失效化处理(包括由 [`Pkg.update`] 执行的修改,此外在 [`Pkg.rm`] 执行后也没有清理操作) - (including by `Pkg.update`), and no cleanup is done after `Pkg.rm` +1. 在源代码文件本身被修改之后,不会执行代码重载或缓存失效化处理(包括由 `Pkg.update` 执行的修改,此外在 `Pkg.rm` 执行后也没有清理操作) + 2. 变形数组的内存共享特性会被预编译忽略(每个数组样貌都会获得一个拷贝) 3. 文件系统在编译期间和运行期间被假设为不变的,比如使用 [`@__FILE__`](@ref)/`source_path()` 在运行期间寻找资源、或使用 BinDeps 宏 `@checked_lib`。有时这是不可避免的。但是可能的话,在编译期将资源复制到模块里面是个好做法,这样在运行期间,就不需要去寻找它们了。 @@ -269,10 +251,10 @@ at compile-time. 4. `WeakRef` 对象和完成器目前在序列化器中无法被恰当地处理(在接下来的发行版中将修复)。 -5. 通常,最好避免去捕捉内部元数据对象的引用,如 `Method`、`MethodInstance`、`TypeMapLevel`、`TypeMapEntry` 及这些对象的字段,因为这会迷惑序列化器,且可能会引发你不想要的结果。此操作不足以成为一个错误,但你需做好准备:系统会尝试拷贝一部分,然后创建其余部分的单个独立对象。 +5. 通常,最好避免去捕捉内部元数据对象的引用,如 `Method`、`MethodInstance`、`TypeMapLevel`、`TypeMapEntry` 及这些对象的字段,因为这会迷惑序列化器,且可能会引发你不想要的结果。此操作不足以成为一个错误,但你需做好准备:系统会尝试拷贝一部分,然后创建其余部分的单个独立实例。 -在开发模块时,关闭增量预编译可能会有所帮助。命令行标记 `--compiled-modules={yes|no}` 可以让你切换预编译的开启和关闭。当 Julia 附加 `--compiled-modules=no` 启动,在载入模块和模块依赖时,编译缓存中的序列化模块会被忽略。`Base.compilecache` 仍可以被手动调用。此命令行标记的状态会被传递给 `Pkg.build`,禁止其在安装、更新、显式建立包时触发自动预编译。 +在开发模块时,关闭增量预编译可能会有所帮助。命令行标记 `--compiled-modules={yes|no}` 可以让你切换预编译的开启和关闭。当 Julia 附加 `--compiled-modules=no` 启动,在载入模块和模块依赖时,编译缓存中的序列化模块会被忽略。`Base.compilecache` 仍可以被手动调用。此命令行标记的状态会被传递给 `Pkg.build`,禁止其在安装、更新、显式构建包时触发自动预编译。 diff --git a/zh_CN/doc/src/manual/noteworthy-differences.md b/zh_CN/doc/src/manual/noteworthy-differences.md index 1d8b9f35..914b12c9 100644 --- a/zh_CN/doc/src/manual/noteworthy-differences.md +++ b/zh_CN/doc/src/manual/noteworthy-differences.md @@ -305,3 +305,25 @@ Julia 的目标之一是为数据分析和统计编程提供高效的语言。 + +## Noteworthy differences from Common Lisp + +- Julia uses 1-based indexing for arrays by default, and it can also handle arbitrary [index offsets](@ref man-custom-indices). + +- Functions and variables share the same namespace (“Lisp-1”). + +- There is a [`Pair`](@ref) type, but it is not meant to be used as a `COMMON-LISP:CONS`. Various iterable collections can be used interchangeably in most parts of the language (eg splatting, tuples, etc). `Tuple`s are the closest to Common Lisp lists for *short* collections of heterogeneous elements. Use `NamedTuple`s in place of alists. For larger collections of homogeneous types, `Array`s and `Dict`s should be used. + +- The typical Julia workflow for prototyping also uses continuous manipulation of the image, implemented with the [Revise.jl](https://github.com/timholy/Revise.jl) package. + +- Bignums are supported, but conversion is not automatic; ordinary integers [overflow](@ref faq-integer-arithmetic). + +- Modules (namespaces) can be hierarchical. [`import`](@ref) and [`using`](@ref) have a dual role: they load the code and make it available in the namespace. `import` for only the module name is possible (roughly equivalent to `ASDF:LOAD-OP`). Slot names don't need to be exported separately. Global variables can't be assigned to from outside the module (except with `eval(mod, :(var = val))` as an escape hatch). + +- Macros start with `@`, and are not as seamlessly integrated into the language as Common Lisp; consequently, macro usage is not as widespread as in the latter. A form of hygiene for [macros](@ref Metaprogramming) is supported by the language. Because of the different surface syntax, there is no equivalent to `COMMON-LISP:&BODY`. + +- *All* functions are generic and use multiple dispatch. Argument lists don't have to follow the same template, which leads to a powerful idiom (see [`do`](@ref)). Optional and keyword arguments are handled differently. Method ambiguities are not resolved like in the Common Lisp Object System, necessitating the definition of a more specific method for the intersection. + +- Symbols do not belong to any package, and do not contain any values *per se*. `M.var` evaluates the symbol `var` in the module `M`. + +- A functional programming style is fully supported by the language, including closures, but isn't always the idiomatic solution for Julia. Some [workarounds](@ref man-performance-captured) may be necessary for performance when modifying captured variables. diff --git a/zh_CN/doc/src/manual/parallel-computing.md b/zh_CN/doc/src/manual/parallel-computing.md index 79a68fa6..cc24acad 100644 --- a/zh_CN/doc/src/manual/parallel-computing.md +++ b/zh_CN/doc/src/manual/parallel-computing.md @@ -448,10 +448,12 @@ Julia 的消息传递机制与一些其它的框架不太一样,比如 MPI [^1 Julia 中的分布式编程基于两个基本概念:**远程引用**(*remote references*)和**远程调用**(*remote calls*)。远程引用是一个对象,任意一个进程可以通过它访问存储在某个特定进程上的对象。远程调用指是某个进程发起的执行函数的请求,该函数会在另一个(也可能是同一个)进程中执行。 -远程引用有两种类型:[`Future`](@ref) 和 [`RemoteChannel`](@ref)。 - -一次远程调用会返回一个 [`Future`](@ref) 作为结果。远程调用会立即返回;也就是说,执行远程调用的进程接下来会继续执行下一个操作,而远程调用则会在另外的进程中进行。你可以通过对返回的 [`Future`](@ref) 执行 [`wait`](@ref) 操作来等待远程调用结束,然后用 [`fetch`](@ref) 获取结果。 +Remote references come in two flavors: [`Future`](@ref Distributed.Future) and [`RemoteChannel`](@ref). +A remote call returns a [`Future`](@ref Distributed.Future) to its result. Remote calls return immediately; the process +that made the call proceeds to its next operation while the remote call happens somewhere else. +You can wait for a remote call to finish by calling [`wait`](@ref) on the returned [`Future`](@ref Distributed.Future), +and you can obtain the full value of the result using [`fetch`](@ref). 对于 [`RemoteChannel`](@ref) 而言,它可以被反复写入。例如,多个进程可以通过引用同一个远程 `Channel` 来协调相互之间的操作。 @@ -490,14 +492,14 @@ julia> remotecall_fetch(getindex, 2, r, 1, 1) 回忆下,这里 [`getindex(r,1,1)`](@ref) [相当于](@ref man-array-indexing) `r[1,1]`,因此,上面的调用相当于获取 `r` 的第一个元素。 -[`remotecall`](@ref) 的语法不是很方便,有一个宏 [`@spawn`](@ref) 可以做些简化,其作用于一个表达式,而不是函数,同时会自动帮你选择在哪个进程上执行。 - +To make things easier, the symbol `:any` can be passed to [`@spawnat`], which picks where to do +the operation for you: ```julia-repl -julia> r = @spawn rand(2,2) +julia> r = @spawnat :any rand(2,2) Future(2, 1, 4, nothing) -julia> s = @spawn 1 .+ fetch(r) +julia> s = @spawnat :any 1 .+ fetch(r) Future(3, 1, 5, nothing) julia> fetch(s) @@ -506,13 +508,34 @@ julia> fetch(s) 1.20939 1.57158 ``` -注意这里执行的是 `1 .+ fetch(r)` 而不是 `1 .+ r`。这是因为我们并不知道这段代码会在哪个进程中执行,因此,通常需要用 [`fetch`](@ref) 将 `r` 中的数据挪到当前计算加法的进程中。这时候 [`@spawn`](@ref) 会很智能地在拥有 `r` 的进程中执行计算,此时,[`fetch`](@ref) 就相当于什么都不用做。(译者注:[issue#28350](https://github.com/JuliaLang/julia/issues/28350)) - -显然,[`@spawn`](@ref) 并非 Julia 内置的一部分,而是通过 [宏](@ref man-macros) 定义的,因此,你也可以自己定义类似的结构。 - -有一点一定要注意,一旦执行了 `fetch`,[`Future`](@ref) 就会将结果缓存起来,之后执行 [`fetch`](@ref) 的时候就不涉及到网络传输了。一旦所有的 [`Future`](@ref) 都获取到了值,那么远端存储的值就会被删掉。 - -[`@async`](@ref) 跟 [`@spawn`](@ref) 有点类似,不过只在当前局部线程中执行。通过它来给每个进程创建一个「喂养」task,每个 task 都选取下一个将要计算的索引,然后等待其执行结束,然后重复该过程,直到索引超出边界。需要注意的是,task 并不会立即执行,只有在执行到 [`@sync`](@ref) 结束时才会开始执行,此时,当前线程交出控制权,直到所有的任务都完成了。在v0.7 之后,所有的喂养 task 都能够通过 `nextidx` 共享状态,因为他们都在同一个进程中。尽管 `Tasks` 是协调调度的,但在某些情况下仍然有可能发送死锁,如 [asynchronous I/O](@ref faq-async-io)。上下文只会在特定时候发生切换,在这里就是执行 [`remotecall_fetch`](@ref)。当然,这是当前版本的实现状态,未来的 Julia 版本中可能会改变,有望在 M 个进程中最多跑 N 个 task,即 [M:N 线程](https://en.wikipedia.org/wiki/Thread_(computing)#Models)。然后,`nextidx` 需要加锁,从而让多个进程能够安全地对一个资源同时进行读写。 +Note that we used `1 .+ fetch(r)` instead of `1 .+ r`. This is because we do not know where the +code will run, so in general a [`fetch`](@ref) might be required to move `r` to the process +doing the addition. In this case, [`@spawnat`](@ref) is smart enough to perform the computation +on the process that owns `r`, so the [`fetch`](@ref) will be a no-op (no work is done). + +(It is worth noting that [`@spawnat`](@ref) is not built-in but defined in Julia as a [macro](@ref man-macros). +It is possible to define your own such constructs.) + +An important thing to remember is that, once fetched, a [`Future`](@ref Distributed.Future) will cache its value +locally. Further [`fetch`](@ref) calls do not entail a network hop. Once all referencing [`Future`](@ref Distributed.Future)s +have fetched, the remote stored value is deleted. + +[`@async`](@ref) is similar to [`@spawnat`](@ref), but only runs tasks on the local process. We +use it to create a "feeder" task for each process. Each task picks the next index that needs to +be computed, then waits for its process to finish, then repeats until we run out of indices. Note +that the feeder tasks do not begin to execute until the main task reaches the end of the [`@sync`](@ref) +block, at which point it surrenders control and waits for all the local tasks to complete before +returning from the function. +As for v0.7 and beyond, the feeder tasks are able to share state via `nextidx` because +they all run on the same process. +Even if `Tasks` are scheduled cooperatively, locking may still be required in some contexts, as in +[asynchronous I/O](@ref faq-async-io). +This means context switches only occur at well-defined points: in this case, +when [`remotecall_fetch`](@ref) is called. This is the current state of implementation and it may change +for future Julia versions, as it is intended to make it possible to run up to N `Tasks` on M `Process`, aka +[M:N Threading](https://en.wikipedia.org/wiki/Thread_(computing)#Models). Then a lock acquiring\releasing +model for `nextidx` will be needed, as it is not safe to let multiple processes read-write a resource at +the same time. @@ -530,7 +553,7 @@ julia> rand2(2,2) 0.153756 0.368514 1.15119 0.918912 -julia> fetch(@spawn rand2(2,2)) +julia> fetch(@spawnat :any rand2(2,2)) ERROR: RemoteException(2, CapturedException(UndefVarError(Symbol("#rand2")) Stacktrace: [...] @@ -632,14 +655,17 @@ julia> addprocs(2) 分布式程序的性能瓶颈主要是由发送消息和数据转移造成的,减少发送消息和转移数据的数量对于获取高性能和可扩展性至关重要,因此,深入了解 Julia 分布式程序是如何转移数据的非常有必要。 -[`fetch`](@ref) 可以看作是显式地转移数据的操作,因为它直接要求获取数据到本地机器。[`@spawn`](@ref)(以及相关的操作)也会移动数据,不过不那么明显,因此称作隐式地数据转移操作。比较以下两种方式,构造一个随机矩阵并求平方: +[`fetch`](@ref) can be considered an explicit data movement operation, since it directly asks +that an object be moved to the local machine. [`@spawnat`](@ref) (and a few related constructs) +also moves data, but this is not as obvious, hence it can be called an implicit data movement +operation. Consider these two approaches to constructing and squaring a random matrix: 方法一: ```julia-repl julia> A = rand(1000,1000); -julia> Bref = @spawn A^2; +julia> Bref = @spawnat :any A^2; [...] @@ -649,20 +675,32 @@ julia> fetch(Bref); 方法二: ```julia-repl -julia> Bref = @spawn rand(1000,1000)^2; +julia> Bref = @spawnat :any rand(1000,1000)^2; [...] julia> fetch(Bref); ``` -二者的差别似乎微乎其微,不过受于 [`@spawn`](@ref) 的实现,二者其实有很大的区别。第一种方法中,首先在本地构造了一个随机矩阵,然后将其发送到另外一个线程计算平方,而第二种方法中,随机矩阵的构造以及求平方计算都在另外一个进程。因此,第二种方法传输的数据要比第一种方法少得多。 - +The difference seems trivial, but in fact is quite significant due to the behavior of [`@spawnat`](@ref). +In the first method, a random matrix is constructed locally, then sent to another process where +it is squared. In the second method, a random matrix is both constructed and squared on another +process. Therefore the second method sends much less data than the first. -在上面这个简单的例子中,两种方法很好区分并作出选择。不过,在实际的程序中设计如何转移数据时,需要经过深思熟虑。例如,如果第一个进程需要使用 `A`,那么第一种方法就更合适些。或者,如果计算 `A` 非常复杂,而所有的进程中又只有当前进程有数据 `A`,那么转移数据 `A` 就不可避免了。又或者,当前进程在 [`@spawn`](@ref) 和 `fetch(Bref)` 之间几乎没什么可做的,那么最好就不用并行了。又比如,假设 `rand(1000,1000)` 操作换成了某种非常复杂的操作,那么也许为这个操作再增加一个 [`@spawn`](@ref) 是个不错的方式。 +In this toy example, the two methods are easy to distinguish and choose from. However, in a real +program designing data movement might require more thought and likely some measurement. For example, +if the first process needs matrix `A` then the first method might be better. Or, if computing +`A` is expensive and only the current process has it, then moving it to another process might +be unavoidable. Or, if the current process has very little to do between the [`@spawnat`](@ref) +and `fetch(Bref)`, it might be better to eliminate the parallelism altogether. Or imagine `rand(1000,1000)` +is replaced with a more expensive operation. Then it might make sense to add another [`@spawnat`](@ref) +statement just for this step. ## 全局变量 -通过 `@spawn` 在远端执行的表达式,或者通过 `remotecall` 调用的闭包,有可能引用全局变量。在 `Main` 模块中的全局绑定和其它模块中的全局绑定有所不同,来看看下面的例子: +Expressions executed remotely via `@spawnat`, or closures specified for remote execution using +`remotecall` may refer to global variables. Global bindings under module `Main` are treated +a little differently compared to global bindings in other modules. Consider the following code +snippet: ```julia-repl A = rand(10,10) @@ -721,7 +759,10 @@ Main Module ## 并行的Map和Loop -幸运的是,许多有用的并行计算并不涉及数据转移。一个典型的例子就是蒙特卡洛模拟,每个进程都独立地完成一些模拟试验。这里用 [`@spawn`](@ref) 在两个进程进行抛硬币的试验,首先,将下面的代码写入 `count_heads.jl` 文件: +Fortunately, many useful parallel computations do not require data movement. A common example +is a Monte Carlo simulation, where multiple processes can handle independent simulation trials +simultaneously. We can use [`@spawnat`](@ref) to flip coins on two processes. First, write the following +function in `count_heads.jl`: ```julia function count_heads(n) @@ -738,10 +779,10 @@ end ```julia-repl julia> @everywhere include_string(Main, $(read("count_heads.jl", String)), "count_heads.jl") -julia> a = @spawn count_heads(100000000) +julia> a = @spawnat :any count_heads(100000000) Future(2, 1, 6, nothing) -julia> b = @spawn count_heads(100000000) +julia> b = @spawnat :any count_heads(100000000) Future(3, 1, 7, nothing) julia> fetch(a)+fetch(b) @@ -750,7 +791,10 @@ julia> fetch(a)+fetch(b) 上面的例子展示了一种非常常见而且有用的并行编程模式,在一些进程中执行多次独立的迭代,然后将它们的结果通过某个函数合并到一起,这个合并操作通常称作**聚合**(*reduction*),也就是一般意义上的**张量降维**(tensor-rank-reducing),比如将一个向量降维成一个数,或者是将一个 tensor 降维到某一行或者某一列等。在代码中,通常具有 `x = f(x, v[i])` 这种形式,其中 `x` 是一个叠加器,`f` 是一个聚合函数,而 `v[i]` 则是将要被聚合的值。一般来说,`f` 要求满足结合律,这样不管执行的顺序如何,都不会影响计算结果。 -前面的代码中,调用 `count_heads` 的方式可以被抽象出来,之前我们显式地调用了两次 [`@spawn`](@ref),这将并行计算限制在了两个进程上,为了将并行计算扩展到任意多进程,可以使用 *parallel for loop* 这种形式,在 Julia 中可以用 [`@distributed`](@ref) 宏来实现: +Notice that our use of this pattern with `count_heads` can be generalized. We used two explicit +[`@spawnat`](@ref) statements, which limits the parallelism to two processes. To run on any number +of processes, we can use a *parallel for loop*, running in distributed memory, which can be written +in Julia using [`@distributed`](@ref) like this: ```julia nheads = @distributed (+) for i = 1:200000000 @@ -793,7 +837,11 @@ end 这里每次迭代都会从共享给每个进程的向量 `a` 中随机选一个样本,然后用来计算 `f`。 -可以看到,如果不需要的话,聚合函数可以省略掉,此时,for 循环会异步执行,将独立的任务发送给所有的进程,然后不用等待执行完成,而是立即返回一个 [`Future`](@ref) 数组,调用者可以在之后的某个时刻通过调用 [`fetch`](@ref) 来等待 [`Future`](@ref) 执行完成,或者通过在并行的 for 循环之前添加一个 [`@sync`](@ref),就像`@sync @distributed for`。 +As you could see, the reduction operator can be omitted if it is not needed. In that case, the +loop executes asynchronously, i.e. it spawns independent tasks on all available workers and returns +an array of [`Future`](@ref Distributed.Future) immediately without waiting for completion. The caller can wait for +the [`Future`](@ref Distributed.Future) completions at a later point by calling [`fetch`](@ref) on them, or wait +for completion at the end of the loop by prefixing it with [`@sync`](@ref), like `@sync @distributed for`. 在一些不需要聚合函数的情况下,我们可能只是像对某个范围内的整数应用一个函数(或者,更一般地,某个序列中的所有元素),这种操作称作**并行的 map**,在 Julia 中有一个对应的函数 [`pmap`](@ref)。例如,可以像下面这样计算一些随机大矩阵的奇异值: @@ -809,7 +857,10 @@ Julia 中的 [`pmap`](@ref) 是被设计用来处理一些计算量比较复杂 远程引用通常指某种 `AbstractChannel` 的实现。 -一个具体的 `AbstractChannel`(有点像 `Channel`)需要将 [`put!`](@ref), [`take!`](@ref), [`fetch`](@ref), [`isready`](@ref) 和 [`wait`](@ref) 都实现。通过 [`Future`](@ref) 引用的远程对象存储在一个 `Channel{Any}(1)` 中(容量为 1,类型为 `Any`)。 +A concrete implementation of an `AbstractChannel` (like `Channel`), is required to implement +[`put!`](@ref), [`take!`](@ref), [`fetch`](@ref), [`isready`](@ref) and [`wait`](@ref). +The remote object referred to by a [`Future`](@ref Distributed.Future) is stored in a `Channel{Any}(1)`, i.e., a +`Channel` of size 1 capable of holding objects of `Any` type. [`RemoteChannel`](@ref) 可以被反复写入,可以指向任意大小和类型的 channel(或者是任意 `AbstractChannel` 的实现)。 @@ -897,19 +948,33 @@ julia> @elapsed while n > 0 # print out results 远程引用所指向的对象可以在其所有引用都被集群删除之后被释放掉。 -存储具体值的节点会记录哪些 worker 已经引用了它。每当某个 [`RemoteChannel`](@ref) 或某个(还没被获取的)[`Future`](@ref) 序列化到一个 worker 中时,会通知相应的节点。而且每当某个 [`RemoteChannel`](@ref) 或某个(还没被获取的)[`Future`](@ref) 被本地的垃圾回收器回收的时候,相应的节点也会收到通知。所有这些都是通过一个集群内部序列化器实现的,而所有的远程引用都只有在运行中的集群才有效,目前序列化和反序列化到 `IO` 暂时还不支持。 +The node where the value is stored keeps track of which of the workers have a reference to it. +Every time a [`RemoteChannel`](@ref) or a (unfetched) [`Future`](@ref Distributed.Future) is serialized to a worker, +the node pointed to by the reference is notified. And every time a [`RemoteChannel`](@ref) or +a (unfetched) [`Future`](@ref Distributed.Future) is garbage collected locally, the node owning the value is again +notified. This is implemented in an internal cluster aware serializer. Remote references are only +valid in the context of a running cluster. Serializing and deserializing references to and from +regular `IO` objects is not supported. 上面说到的**通知**都是通过发送"跟踪"信息来实现的,当一个引用被序列化的时候,就会发送"添加引用"的信息,而一个引用被本地的垃圾回收器回收的时候,就会发送一个"删除引用"的信息。 -由于 [`Future`](@ref) 是一次写入然后换成在本地,因此 [`fetch`](@ref) 一个 [`Future`](@ref) 会向拥有该值的节点发送更新引用的跟踪信息。 +Since [`Future`](@ref Distributed.Future)s are write-once and cached locally, the act of [`fetch`](@ref)ing a +[`Future`](@ref Distributed.Future) also updates reference tracking information on the node owning the value. 一旦指向某个值的引用都被删除了,对应的节点会将其释放。 -对于 [`Future`](@ref) 来说,序列化一个已经获取了值的 [`Future`](@ref) 到另外一个节点时,会将其值也一并序列化过去,因为原始的远端的值可能已经被回收释放了。 +With [`Future`](@ref Distributed.Future)s, serializing an already fetched [`Future`](@ref Distributed.Future) to a different node also +sends the value since the original remote store may have collected the value by this time. 此外需要注意的是,本地的垃圾回收到底发生在什么时候取决于具体对象的大小以及当时系统的内存压力。 -对于远端引用,其引用本身的大小很小,不过在远端节点存储着的值可能相当大。由于本地的对象并不会立即被回收,于是一个比较好的做法是,对本地的 [`RemoteChannel`](@ref) 或者是还没获取值的 [`Future`](@ref) 执行 [`finalize`](@ref)。对于已经获取了值的 [`Future`](@ref) 来说,由于已经在调用 [`fetch`](@ref) 的时候已经将引用删除了,因此就不必再 [`finalize`](@ref) 了。显式地调用 [`finalize`](@ref) 会立即向远端节点发送信息并删除其引用。 +In case of remote references, the size of the local reference object is quite small, while the +value stored on the remote node may be quite large. Since the local object may not be collected +immediately, it is a good practice to explicitly call [`finalize`](@ref) on local instances +of a [`RemoteChannel`](@ref), or on unfetched [`Future`](@ref Distributed.Future)s. Since calling [`fetch`](@ref) +on a [`Future`](@ref Distributed.Future) also removes its reference from the remote store, this is not required on +fetched [`Future`](@ref Distributed.Future)s. Explicitly calling [`finalize`](@ref) results in an immediate message +sent to the remote node to go ahead and remove its reference to the value. 一旦执行了 finalize 之后,引用就不可用了。 @@ -917,7 +982,7 @@ julia> @elapsed while n > 0 # print out results ## Local invocations(@id man-distributed-local-invocations) Data is necessarily copied over to the remote node for execution. This is the case for both -remotecalls and when data is stored to a[`RemoteChannel`](@ref) / [`Future`](@ref) on +remotecalls and when data is stored to a[`RemoteChannel`](@ref) / [`Future`](@ref Distributed.Future) on a different node. As expected, this results in a copy of the serialized objects on the remote node. However, when the destination node is the local node, i.e. the calling process id is the same as the remote node id, it is executed diff --git a/zh_CN/doc/src/manual/performance-tips.md b/zh_CN/doc/src/manual/performance-tips.md index f5d113b8..fb5d9c41 100644 --- a/zh_CN/doc/src/manual/performance-tips.md +++ b/zh_CN/doc/src/manual/performance-tips.md @@ -1281,3 +1281,11 @@ function abmult3(r::Int) end ``` `let` 代码块创建了一个新的变量 `r`,它的作用域只是内部函数。第二种技术在捕获变量存在时完全恢复了语言性能。请注意,这是编译器的一个快速发展的方面,未来的版本可能不需要依靠这种程度的程序员注释来获得性能。与此同时,一些用户提供的包(如 [FastClosures](https://github.com/c42f/FastClosures.jl))会自动插入像在 `abmult3` 中那样的 `let` 语句。 + +# Checking for equality with a singleton + +When checking if a value is equal to some singleton it can be +better for performance to check for identicality (`===`) instead of +equality (`==`). The same advice applies to using `!==` over `!=`. +These type of checks frequently occur e.g. when implementing the iteration +protocol and checking if `nothing` is returned from [`iterate`](@ref). diff --git a/zh_CN/doc/src/manual/profile.md b/zh_CN/doc/src/manual/profile.md index 4973645f..884da0b5 100644 --- a/zh_CN/doc/src/manual/profile.md +++ b/zh_CN/doc/src/manual/profile.md @@ -10,8 +10,7 @@ 尽管有这些限制,但抽样分析器仍然有很大的优势: - * 你无需对代码进行修改即可进行定时测量(这与替代的[注入型分析器](https://github.com/timholy/IProfile.jl)相反)。 - + * You do not have to make any modifications to your code to take timing measurements. * 它可以分析 Julia 的核心代码,甚至(可选)可以分析 C 和 Fortran 库。 * 通过「偶尔」运行,它只有很少的性能开销;代码在性能分析时能以接近本机的速度运行。 diff --git a/zh_CN/doc/src/manual/strings.md b/zh_CN/doc/src/manual/strings.md index 96b72380..63cf6dfe 100644 --- a/zh_CN/doc/src/manual/strings.md +++ b/zh_CN/doc/src/manual/strings.md @@ -170,18 +170,17 @@ julia> str[end÷2] ' ': ASCII/Unicode U+0020 (category Zs: Separator, space) ``` -使用小于 1 或大于 `end` 的索引会引发错误: +Using an index less than 1 or greater than `end` raises an error: ```jldoctest helloworldstring julia> str[0] -ERROR: BoundsError: attempt to access "Hello, world.\n" +ERROR: BoundsError: attempt to access String at index [0] [...] julia> str[end+1] -ERROR: BoundsError: attempt to access "Hello, world.\n" +ERROR: BoundsError: attempt to access String at index [15] -Stacktrace: [...] ``` diff --git a/zh_CN/stdlib/Dates/docs/src/index.md b/zh_CN/stdlib/Dates/docs/src/index.md index 738db3a5..3ec930f5 100644 --- a/zh_CN/stdlib/Dates/docs/src/index.md +++ b/zh_CN/stdlib/Dates/docs/src/index.md @@ -1,16 +1,10 @@ -# Dates +# 日期 ```@meta DocTestSetup = :(using Dates) ``` -The `Dates` module provides two types for working with dates: [`Date`](@ref) and [`DateTime`](@ref), -representing day and millisecond precision, respectively; both are subtypes of the abstract [`TimeType`](@ref). -The motivation for distinct types is simple: some operations are much simpler, both in terms of -code and mental reasoning, when the complexities of greater precision don't have to be dealt with. -For example, since the [`Date`](@ref) type only resolves to the precision of a single date (i.e. -no hours, minutes, or seconds), normal considerations for time zones, daylight savings/summer -time, and leap seconds are unnecessary and avoided. +`Dates` 模块提供了两种类型来处理日期:[`Date`](@ref) 和 [`DateTime`](@ref),分别精确到日和毫秒;两者都是抽象类型 [`TimeType`](@ref) 的子类型。区分类型的动机很简单:不必处理更高精度所带来的复杂性时,一些操作在代码和思维推理上都更加简单。例如,由于 [`Date`](@ref) 类型仅精确到日(即没有时、分或秒),因此避免了时区、夏令时和闰秒等不必要的通常考虑。 Both [`Date`](@ref) and [`DateTime`](@ref) are basically immutable [`Int64`](@ref) wrappers. The single `instant` field of either type is actually a `UTInstant{P}` type, which @@ -39,8 +33,7 @@ BC/BCE, etc. ## 构造函数 -[`Date`](@ref) and [`DateTime`](@ref) types can be constructed by integer or [`Period`](@ref) -types, by parsing, or through adjusters (more on those later): +[`Date`](@ref) 和 [`DateTime`](@ref) 类型可以通过整数或 [`Period`](@ref) 类型,解析,或调整器来构造(稍后会详细介绍): ```jldoctest julia> DateTime(2013) @@ -127,7 +120,7 @@ julia> for i = 1:10^5 A full suite of parsing and formatting tests and examples is available in [`stdlib/Dates/test/io.jl`](https://github.com/JuliaLang/julia/blob/master/stdlib/Dates/test/io.jl). -## Durations/Comparisons +## 持续时间/比较 Finding the length of time between two [`Date`](@ref) or [`DateTime`](@ref) is straightforward given their underlying representation as `UTInstant{Day}` and `UTInstant{Millisecond}`, respectively. @@ -336,7 +329,7 @@ Stacktrace: ``` -## TimeType-Period Arithmetic +## TimeType 时间运算 It's good practice when using any language/date framework to be familiar with how date-period arithmetic is handled as there are some [tricky issues](https://codeblog.jonskeet.uk/2010/12/01/the-joys-of-date-time-arithmetic/) @@ -395,7 +388,7 @@ results, but otherwise, everything should work as expected. Thankfully, that's p extent of the odd cases in date-period arithmetic when dealing with time in UT (avoiding the "joys" of dealing with daylight savings, leap seconds, etc.). -As a bonus, all period arithmetic objects work directly with ranges: +另外,所有时间运算都可以与范围一起使用: ```jldoctest julia> dr = Date(2014,1,29):Day(1):Date(2014,2,3) @@ -424,7 +417,7 @@ julia> collect(dr) 2014-07-29 ``` -## Adjuster Functions +## 调整器函数 As convenient as date-period arithmetic is, often the kinds of calculations needed on dates take on a *calendrical* or *temporal* nature rather than a fixed number of periods. Holidays are @@ -503,7 +496,7 @@ julia> filter(dr) do x Additional examples and tests are available in [`stdlib/Dates/test/adjusters.jl`](https://github.com/JuliaLang/julia/blob/master/stdlib/Dates/test/adjusters.jl). -## Period Types +## 时间段类型 Periods are a human view of discrete, sometimes irregular durations of time. Consider 1 month; it could represent, in days, a value of 28, 29, 30, or 31 depending on the year and month context. @@ -538,7 +531,7 @@ julia> div(y3,3) # 镜像整数除法 3 years ``` -## Rounding +## 取整 [`Date`](@ref) and [`DateTime`](@ref) values can be rounded to a specified resolution (e.g., 1 month or 15 minutes) with [`floor`](@ref), [`ceil`](@ref), or [`round`](@ref): @@ -627,7 +620,7 @@ on methods exported from the `Dates` module. # [API reference](@id stdlib-dates-api) -## Dates and Time Types +## 日期和时间类型 ```@docs Dates.Period @@ -640,7 +633,7 @@ Dates.Date Dates.Time ``` -## Dates Functions +## 日期函数 ```@docs Dates.DateTime(::Int64, ::Int64, ::Int64, ::Int64, ::Int64, ::Int64, ::Int64) @@ -742,7 +735,7 @@ Dates.CompoundPeriod(::Vector{<:Dates.Period}) Dates.default ``` -### Rounding Functions +### 取整函数 `Date` and `DateTime` values can be rounded to a specified resolution (e.g., 1 month or 15 minutes) with `floor`, `ceil`, or `round`. @@ -771,7 +764,7 @@ Dates.date2epochdays Dates.datetime2epochms ``` -### Conversion Functions +### 转换函数 ```@docs Dates.today @@ -783,7 +776,7 @@ Dates.rata2datetime Dates.datetime2rata ``` -### Constants +### 常量 Days of the Week: diff --git a/zh_CN/stdlib/Distributed/docs/src/index.md b/zh_CN/stdlib/Distributed/docs/src/index.md index eeda0c34..ee185bb5 100644 --- a/zh_CN/stdlib/Distributed/docs/src/index.md +++ b/zh_CN/stdlib/Distributed/docs/src/index.md @@ -57,10 +57,8 @@ Distributed.cluster_cookie(::Any) ## 集群管理接口 -This interface provides a mechanism to launch and manage Julia workers on different cluster environments. -There are two types of managers present in Base: `LocalManager`, for launching additional workers on the -same host, and `SSHManager`, for launching on remote hosts via `ssh`. TCP/IP sockets are used to connect -and transport messages between processes. It is possible for Cluster Managers to provide a different transport. +这个接口提供了一种在不同的集群上启动和管理 Julia 工作节点的机制。 +Base 模块提供了两种类型的管理器:`Localmanager` 用于在同一台主机上启动额外的工作节点; `SSHManager` 用于通过 `ssh` 在远程主机上启动额外的工作节点。 TCP/IP socket 用于连接进程以及进程间的信息传递。集群管理器也可以提供一种不同的传递方式。 ```@docs Distributed.launch diff --git a/zh_CN/stdlib/Random/docs/src/index.md b/zh_CN/stdlib/Random/docs/src/index.md index a48ba2e0..a863c2bb 100644 --- a/zh_CN/stdlib/Random/docs/src/index.md +++ b/zh_CN/stdlib/Random/docs/src/index.md @@ -10,21 +10,16 @@ can be plugged in by inheriting the `AbstractRNG` type; they can then be used to streams of random numbers. Besides `MersenneTwister`, Julia also provides the `RandomDevice` RNG type, which is a wrapper over the OS provided entropy. -Most functions related to random generation accept an optional `AbstractRNG` object as first argument, -which defaults to the global one if not provided. Moreover, some of them accept optionally -dimension specifications `dims...` (which can be given as a tuple) to generate arrays of random -values. +大部分与随机数生成相关的函数都接受一个可选的 `AbstractRNG` 对象作为第一个参数,如果不指定则使用全局默认的。此外,某些函数还接受一个可选的维度参数 `dims...` (可以是元组)来生成随机数组。 -A `MersenneTwister` or `RandomDevice` RNG can generate uniformly random numbers of the following types: +一个 `MersenneTwister` 或 `RandomDevice` RNG 能够生成如下类型的随机数: [`Float16`](@ref), [`Float32`](@ref), [`Float64`](@ref), [`BigFloat`](@ref), [`Bool`](@ref), [`Int8`](@ref), [`UInt8`](@ref), [`Int16`](@ref), [`UInt16`](@ref), [`Int32`](@ref), [`UInt32`](@ref), [`Int64`](@ref), [`UInt64`](@ref), [`Int128`](@ref), [`UInt128`](@ref), -[`BigInt`](@ref) (or complex numbers of those types). -Random floating point numbers are generated uniformly in ``[0, 1)``. As `BigInt` represents -unbounded integers, the interval must be specified (e.g. `rand(big.(1:6))`). +[`BigInt`](@ref) (或者这些类型的复数)。 +随机浮点数在 ``[0, 1)`` 区间均匀生成。由于 `BigInt` 代表无界的整数,必须要指定区间(如 `rand(big.(1:6))`)。 -Additionally, normal and exponential distributions are implemented for some `AbstractFloat` and -`Complex` types, see [`randn`](@ref) and [`randexp`](@ref) for details. +另外,正态和指数分布是针对某些 `AbstractFloat` 和 `Complex` 类型,详细内容见 [`randn`](@ref) 和 [`randexp`](@ref)。 ## Random generation functions @@ -63,9 +58,9 @@ Random.RandomDevice ## Hooking into the `Random` API -There are two mostly orthogonal ways to extend `Random` functionalities: -1) generating random values of custom types -2) creating new generators +有两种主要的正交的方式可以扩展 `Random` 的功能: +1) 为自定义类型生成随机值 +2) 创造新的生成器 The API for 1) is quite functional, but is relatively recent so it may still have to evolve in subsequent releases of the `Random` module. For example, it's typically sufficient to implement one `rand` method in order to have all other usual methods work automatically.