文件

存取文件

可在 REPL 或 IJulia 中輸入 ? 後接函數或巨集名稱,並按下 Enter 來存取文件。例如,

?cos
?@time
?r""

將分別顯示相關函數、巨集或字串巨集的文件。大多數 Julia 環境提供直接存取文件的方法

  • VS Code 在您將滑鼠游標移到函數名稱上時會顯示文件。您也可以使用側邊欄中的 Julia 面板來搜尋文件。
  • Pluto 中,開啟右下角的「Live Docs」面板。
  • Juno 中使用 Ctrl-J, Ctrl-D 將顯示游標下的物件文件。

撰寫文件

Julia 讓套件開發人員和使用者可以透過內建文件系統輕鬆地記錄函數、類型和其他物件。

基本語法很簡單:任何出現在物件(函數、巨集、類型或實例)之前的字串都會被解釋為記錄它(這些稱為文件字串)。請注意,文件字串和所記錄的物件之間不能有空白行或註解。以下是一個基本範例

"Tell whether there are too foo items in the array."
foo(xs::Array) = ...

文件被解釋為 Markdown,因此您可以使用縮排和程式碼圍欄來區分程式碼範例和文字。技術上來說,任何物件都可以與任何其他物件關聯為元資料;Markdown 碰巧是預設值,但也可以建構其他字串巨集並將它們傳遞給 @doc 巨集。

注意

Markdown 支援已在 Markdown 標準函式庫中實作,有關支援語法的完整清單,請參閱 文件

以下是一個更複雜的範例,仍然使用 Markdown

"""
    bar(x[, y])

Compute the Bar index between `x` and `y`.

If `y` is unspecified, compute the Bar index between all pairs of columns of `x`.

# Examples
```julia-repl
julia> bar([1, 2], [1, 2])
1
```
"""
function bar(x, y) ...

如同上面的範例,我們建議在撰寫文件時遵循一些簡單的慣例

  1. 請務必在文件最上方顯示函數簽章,並使用四個空格縮排,以便列印為 Julia 程式碼。

    此簽章可以與 Julia 程式碼中的簽章相同(例如 mean(x::AbstractArray)),或為簡化形式。如果可行,應以其預設值表示選用參數(例如 f(x, y=1)),遵循實際 Julia 語法。沒有預設值的選用參數應置於括弧中(例如 f(x[, y])f(x[, y[, z]]))。另一種解決方案是使用多行:一行不含選用參數,其他行則包含選用參數。此解決方案也可用於記錄給定函數的數個相關方法。當函數接受多個關鍵字參數時,請僅在簽章中包含 <keyword arguments> 佔位符(例如 f(x; <keyword arguments>)),並在 # Arguments 區段下方提供完整清單(請參閱下方的第 4 點)。

  2. 在簡化簽章區塊後,請包含一個單行句子,說明函數的作用或物件的表示方式。如果需要,請在空白行後,於第二段提供更多詳細資訊。

    記錄函數時,單行句子應使用命令式(「執行此操作」、「傳回那個」),而非第三人稱(請勿撰寫「傳回長度...」)。句子應以句號結尾。如果無法輕易總結函數的意義,將其拆分成可組合的個別部分可能有所幫助(但這不應視為每個案例的絕對要求)。

  3. 請勿重複說明。

    由於函數名稱是由簽章給出的,因此無需以「函數 bar...」開始文件說明:直接切入重點。類似地,如果簽章指定參數的類型,在說明中提到它們是多餘的。

  4. 僅在確實需要時提供參數清單。

    對於簡單的函數,通常在函數目的說明中直接提到參數的角色會更清楚。參數清單只會重複其他地方已經提供的資訊。但是,對於具有許多參數(特別是關鍵字參數)的複雜函數,提供參數清單會是個好主意。在這種情況下,在函數的一般說明之後,在 # Arguments 標題下插入它,每個參數有一個 - 項目符號。清單應提到參數的類型和預設值(如果有)。

    """
    ...
    # Arguments
    - `n::Integer`: the number of elements to compute.
    - `dim::Integer=1`: the dimensions along which to perform the computation.
    ...
    """
  5. 提供與相關函數的提示。

    有時會有相關功能的函數。為了增加可發現性,請在 See also 段落中提供這些函數的簡短清單。

    See also [`bar!`](@ref), [`baz`](@ref), [`baaz`](@ref).
  6. # Examples 區段中包含任何程式碼範例。

    範例應盡可能寫成 doctestsdoctest 是以 ```jldoctest 開頭的圍欄式程式碼區塊(請參閱 程式碼區塊),包含任意數量的 julia> 提示,以及輸入和預期的輸出,模擬 Julia REPL。

    注意

    Doctests 由 Documenter.jl 啟用。有關更詳細的文件說明,請參閱 Documenter 的 手冊

    例如,在以下文件字串中,定義了一個變數 a,然後出現預期的結果,就像在 Julia REPL 中列印的一樣

    """
    Some nice documentation here.
    
    # Examples
    ```jldoctest
    julia> a = [1 2; 3 4]
    2×2 Array{Int64,2}:
     1  2
     3  4
    ```
    """
    警告

    在 doctest 中應避免呼叫 rand 和其他 RNG 相關函數,因為它們在不同的 Julia 會話期間不會產生一致的輸出。如果您想展示一些隨機數生成相關功能,一個選項是明確建構並設定您自己的 RNG 物件(請參閱 Random),並將其傳遞給您正在 doctest 的函數。

    作業系統字元大小(Int32Int64)以及路徑分隔符號差異(/\)也會影響某些 doctest 的可重製性。

    請注意,doctest 中的空白很重要!例如,如果您錯誤對齊陣列漂亮列印的輸出,doctest 將會失敗。

    然後您可以執行 make -C doc doctest=true 以執行 Julia 手冊和 API 文件中的所有 doctest,這將確保您的範例有效。

    若要指出輸出結果已截斷,您可以在檢查應停止的行中寫入 [...]。當 doctest 顯示已引發例外時,這對於隱藏堆疊追蹤(包含對 julia 程式碼行的非永久性參考)很有用,例如

    ```jldoctest
    julia> div(1, 0)
    ERROR: DivideError: integer division error
    [...]
    ```

    不可測試的範例應寫在以 ```julia 開頭的圍欄式程式碼區塊中,以便在產生的文件中正確地加以突顯。

    提示

    範例應盡可能自給自足可執行,以便讀者能夠在不包含任何依賴項的情況下試用它們。

  7. 使用反引號來識別程式碼和方程式。

    Julia 識別碼和程式碼摘錄應始終出現在反引號 ` 之間,以啟用突顯。LaTeX 語法中的方程式可以插入在雙反引號 `` 之間。使用 Unicode 字元,而不是它們的 LaTeX 逸出序列,即 ``α = 1`` 而不是 ``\\alpha = 1``

  8. 將開始和結束的 """ 字元放在單獨的行上。

    也就是說,寫

    """
    ...
    
    ...
    """
    f(x, y) = ...

    而不是

    """...
    
    ..."""
    f(x, y) = ...

    這使得文件字串的開始和結束位置更清楚。

  9. 遵守周圍程式碼使用的行長度限制。

    文件字串使用與程式碼相同的工具編輯。因此,應套用相同的慣例。建議每行寬度最多為 92 個字元。

  10. # Implementation 區段提供資訊,讓自訂類型可以在函式中實作。這些實作細節是針對開發人員而非使用者,例如說明應覆寫哪些函式,以及哪些函式會自動使用適當的後備。此類細節最好與函式行為的主要說明分開。

  11. 對於較長的說明文件,請考慮使用 # Extended help 標題分割說明文件。典型的說明模式只會顯示標題上方的資料;您可以在表達式的開頭加上「?」來存取完整說明(也就是「??foo」而不是「?foo」)。

函式與方法

Julia 中的函式可能有多個實作,稱為方法。雖然讓一般函式只有一個目的會是很好的做法,但 Julia 允許在必要時個別說明方法。一般來說,只應說明最一般的函式,甚至函式本身(也就是透過 function bar end 建立的物件,不含任何方法)。只有當特定方法的行為與較一般的方法不同時,才應說明該方法。無論如何,它們不應重複其他地方提供的資訊。例如

"""
    *(x, y, z...)

Multiplication operator. `x * y * z *...` calls this function with multiple
arguments, i.e. `*(x, y, z...)`.
"""
function *(x, y, z...)
    # ... [implementation sold separately] ...
end

"""
    *(x::AbstractString, y::AbstractString, z::AbstractString...)

When applied to strings, concatenates them.
"""
function *(x::AbstractString, y::AbstractString, z::AbstractString...)
    # ... [insert secret sauce here] ...
end

help?> *
search: * .*

  *(x, y, z...)

  Multiplication operator. x * y * z *... calls this function with multiple
  arguments, i.e. *(x,y,z...).

  *(x::AbstractString, y::AbstractString, z::AbstractString...)

  When applied to strings, concatenates them.

在擷取通用函數文件時,每個方法的元資料會與 catdoc 函數串接,當然也可以為自訂類型覆寫此函數。

進階用法

@doc 巨集會將其第一個引數與第二個引數關聯在稱為 META 的每個模組字典中。

為了讓撰寫文件更輕鬆,剖析器會特別處理巨集名稱 @doc:如果對 @doc 的呼叫只有一個引數,但另一個表達式出現在單行中斷後,則會將該額外表達式新增為巨集的引數。因此,剖析器會將下列語法剖析為對 @doc 的 2 個引數呼叫

@doc raw"""
...
"""
f(x) = x

這樣就能使用除了正常字串文字 (例如 raw"" 字串巨集) 以外的表達式作為文件字串。

當用於擷取文件時,@doc 巨集 (或等同的 doc 函數) 會搜尋所有 META 字典,以取得與給定物件相關的元資料並傳回。傳回的物件 (例如一些 Markdown 內容) 預設會以智慧的方式顯示自身。此設計也讓文件系統能輕易以程式化方式使用;例如,在函數的不同版本之間重複使用文件

@doc "..." foo!
@doc (@doc foo!) foo

或與 Julia 的元程式設計功能搭配使用

for (f, op) in ((:add, :+), (:subtract, :-), (:multiply, :*), (:divide, :/))
    @eval begin
        $f(a,b) = $op(a,b)
    end
end
@doc "`add(a,b)` adds `a` and `b` together" add
@doc "`subtract(a,b)` subtracts `b` from `a`" subtract

非頂層區塊中的文件,例如 beginifforlet,也應該透過 @doc 新增到文件系統。例如

if condition()
    @doc "..."
    f(x) = x
end

condition()true 時,會將文件新增到 f(x)。請注意,即使 f(x) 在區塊結束時超出範圍,其文件仍會保留。

可以利用元程式設計協助建立文件。在文件字串中使用字串內插時,需要使用額外的 $,如 $($name) 所示

for func in (:day, :dayofmonth)
    name = string(func)
    @eval begin
        @doc """
            $($name)(dt::TimeType) -> Int64

        The day of month of a `Date` or `DateTime` as an `Int64`.
        """ $func(dt::Dates.TimeType)
    end
end

動態文件

有時,類型實例的適當文件取決於該實例的欄位值,而不仅仅取決於類型本身。在這些情況下,您可以為自訂類型新增一個方法到 Docs.getdoc,以逐實例傳回文件。例如,

struct MyType
    value::Int
end

Docs.getdoc(t::MyType) = "Documentation for MyType with value $(t.value)"

x = MyType(1)
y = MyType(2)

?x 會顯示「值為 1 的 MyType 文件」,而 ?y 會顯示「值為 2 的 MyType 文件」。

語法指南

本指南提供全面的概觀,說明如何將文件附加到所有 Julia 語法結構,這些結構可以提供文件。

在以下範例中,"..." 用於說明任意文件字串。

$\ 字元

$\ 字元在文件字串中仍解析為字串內插或跳脫序列的開頭。raw"" 字串巨集與 @doc 巨集可一同使用,以避免必須跳脫這些字元。當文件字串包含包含內插的 LaTeX 或 Julia 原始碼範例時,這很方便

@doc raw"""
```math
\LaTeX
```
"""
function f end

函式和方法

"..."
function f end

"..."
f

將文件字串 "..." 新增至函式 f。第一個版本是首選語法,但兩個版本都是等效的。

"..."
f(x) = x

"..."
function f(x)
    x
end

"..."
f(x)

將文件字串 "..." 新增至方法 f(::Any)

"..."
f(x, y = 1) = x + y

將文件字串 "..." 新增至兩個 Method,即 f(::Any)f(::Any, ::Any)

巨集

"..."
macro m(x) end

將文件字串 "..." 新增至 @m(::Any) 巨集定義。

"..."
:(@m)

將文件字串 "..." 加入名為 @m 的巨集。

類型

"..."
abstract type T1 end

"..."
mutable struct T2
    ...
end

"..."
struct T3
    ...
end

將文件字串 "..." 加入類型 T1T2T3

"..."
struct T
    "x"
    x
    "y"
    y
end

將文件字串 "..." 加入類型 T,將 "x" 加入欄位 T.x,將 "y" 加入欄位 T.y。此規則也適用於 mutable struct 類型。

模組

"..."
module M end

module M

"..."
M

end

將文件字串 "..." 加入 Module M。建議在 Module 上方加入文件字串,但這兩種語法是等效的。

"..."
baremodule M
# ...
end

baremodule M

import Base: @doc

"..."
f(x) = x

end

透過在表達式上方放置文件字串來記錄 baremodule,會自動將 @doc 匯入模組。當模組表達式未記錄時,必須手動完成這些匯入。

全域變數

"..."
const a = 1

"..."
b = 2

"..."
global c = 3

將文件字串 "..." 加入 Binding abc

Binding 用於儲存對 Module 中特定 Symbol 的參照,而不會儲存參照值本身。

注意

const 定義僅用於定義另一個定義的別名時,例如 Base 中的函式 div 及其別名 ÷,請勿記錄別名,而應記錄實際函式。

如果記錄別名而非實際定義,則文件系統(? 模式)在搜尋實際定義時,不會傳回附加至別名的文件字串。

例如,您應該撰寫

"..."
f(x) = x + 1
const alias = f

而不是

f(x) = x + 1
"..."
const alias = f
"..."
sym

將文件字串 "..." 新增至與 sym 相關聯的值。不過,建議在定義 sym 的地方記錄文件。

多個物件

"..."
a, b

將文件字串 "..." 新增至 ab,每個都應該是可以記錄文件運算式。此語法等於

"..."
a

"..."
b

任何數量的運算式都可以用這種方式一起記錄文件。當兩個函數相關聯時,此語法會很有用,例如非變異和變異版本 ff!

巨集產生的程式碼

"..."
@m expression

將文件字串 "..." 新增至透過展開 @m expression 產生的運算式。這允許使用 @inline@noinline@generated 或任何其他巨集裝飾的運算式,以與未裝飾的運算式相同的方式記錄文件。

巨集作者應注意,只有產生單一運算式的巨集才會自動支援文件字串。如果巨集傳回包含多個子運算式的區塊,則必須使用 @__doc__ 巨集標記應記錄文件的子運算式。

@enum 巨集利用 @__doc__ 來允許記錄文件 Enum。檢視其定義應作為如何正確使用 @__doc__ 的範例。

Core.@__doc__巨集
@__doc__(ex)

用於標記巨集所回傳的表達式,並應予以記錄的低階巨集。如果標記了多個表達式,則相同的文件字串會套用至每個表達式。

macro example(f)
    quote
        $(f)() = 0
        @__doc__ $(f)(x) = 1
        $(f)(x, y) = 2
    end |> esc
end

當使用它的巨集未記錄時,@__doc__ 沒有作用。

來源