Wait the light to fall

Julia 中的 REPL

焉知非鱼

REPL in Julia

Julia REPL #

Julia 在 julia 可执行文件中内置了一个功能齐全的交互式命令行 REPL(read-eval-print loop)。除了允许快速、简单地评估 Julia 语句外,它还具有可搜索的历史记录、tab-补全、许多有用的键绑定以及专门的帮助和 shell 模式。REPL 可以通过简单地调用 julia 而不使用参数或双击可执行文件来启动。

$ julia

               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.5.0 (2020-08-01)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |


julia>

要退出交互式会话,请键入 ^D - 控制键与 d 键一起在空行上键入,或键入 exit() 后跟回车或回车键。REPL 会用一个横幅和 julia> 提示来欢迎您。

不同的提示模式 #

朱利安模式 #

REPL 有四种主要的操作模式。第一种也是最常见的是 Julian 提示。这是默认的操作模式;每个新行都以 julia> 开始。在这里您可以输入 Julia 表达式。在输入完整的表达式后点击回车或回车将评估该条目并显示最后一个表达式的结果。

julia> string(1 + 2)
"3"

在交互式工作中,有许多独特的有用功能。除了显示结果之外,REPL 还将结果绑定到变量 ans 上。行上的分号可以作为一个标志来抑制显示结果。

julia> string(3 * 4);

julia> ans
"12"

在 Julia 模式下,REPL 支持称为提示粘贴(prompt pasting)的东西。当把以 julia> 开头的文本粘贴到 REPL 中时,这个功能会被激活。在这种情况下,只有以 julia> 开头的表达式会被解析,其他表达式会被删除。这使得您可以粘贴从 REPL 会话中复制的代码块,而无需清除提示和输出。这个功能默认是启用的,但可以通过 REPL.enable_promptpaste(::Bool) 来禁用或启用。如果启用了,您可以直接将本段上面的代码块粘贴到 REPL 中试试。这个功能在标准的 Windows 命令提示符上不起作用,因为它在检测粘贴发生时的局限性。

using REPL
REPL.enable_promptpaste(false) # 禁用 prompt pasting
REPL.enable_promptpaste(true)  # 启用 prompt pasting

对象在 REPL 中使用带有特定 IOContextshow 函数进行打印。特别是,:limit 属性被设置为 true。其他属性可以在某些 show 方法中接收一个默认值,如果它还没有被设置,比如 :compact。作为实验性功能,可以通过 Base.active_repl.options.iocontext 字典来指定 REPL 使用的属性(将值关联到属性)。例如:

julia> rand(2, 2)
2×2 Array{Float64,2}:
 0.8833    0.329197
 0.719708  0.59114

julia> show(IOContext(stdout, :compact => false), "text/plain", rand(2, 2))
 0.43540323669187075  0.15759787870609387
 0.2540832269192739   0.4597637838786053
julia> Base.active_repl.options.iocontext[:compact] = false;

julia> rand(2, 2)
2×2 Array{Float64,2}:
 0.2083967319174056  0.13330606013126012
 0.6244375177790158  0.9777957560761545

为了在启动时自动定义这个字典的值,可以使用 ~/.julia/config/startup.jl 文件中的 atreplinit 函数,例如:

atreplinit() do repl
    repl.options.iocontext[:compact] = false
end

帮助模式 #

当光标在行首时,可以通过键入 ? 来将提示变为帮助模式。Julia 将尝试打印帮助模式下输入的任何内容的帮助或文档。

julia> ? # upon typing ?, the prompt changes (in place) to: help?>

help?> string
search: string String Cstring Cwstring RevString randstring bytestring SubString

  string(xs...)

  Create a string from any values using the print function.

也可以查询宏、类型和变量:

help?> @time
  @time

  A macro to execute an expression, printing the time it took to execute, the number of allocations,
  and the total number of bytes its execution caused to be allocated, before returning the value of the
  expression.

  See also @timev, @timed, @elapsed, and @allocated.

help?> Int32
search: Int32 UInt32

  Int32 <: Signed

  32-bit signed integer type.

按行首的退格键可以退出帮助模式。

Shell 模式 #

就像帮助模式对于快速访问文档很有用一样,另一个常见的任务是使用系统 shell 来执行系统命令。就像 ? 进入帮助模式时一样, 在行首按下分号(;)会进入 shell 模式。而且可以在行首按退格键退出。

julia> ; # upon typing ;, the prompt changes (in place) to: shell>

shell> echo hello
hello

注意:

对于 Windows 用户来说,Julia 的 shell 模式不暴露 windows shell 命令。因此,这将会失败:

julia> ; # upon typing ;, the prompt changes (in place) to: shell>

shell> dir
ERROR: IOError: could not spawn `dir`: no such file or directory (ENOENT)
Stacktrace!
.......

不过,你可以像这样访问 PowerShell:

julia> ; # upon typing ;, the prompt changes (in place) to: shell>

shell> powershell
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
PS C:\Users\elm>

…而且对 cmd.exe 的访问是这样的(见 dir 命令):

julia> ; # upon typing ;, the prompt changes (in place) to: shell>

shell> cmd
Microsoft Windows [version 10.0.17763.973]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Users\elm>dir
 Volume in drive C has no label
 Volume Serial Number is 1643-0CD7
  Directory of C:\Users\elm

29/01/2020  22:15    <DIR>          .
29/01/2020  22:15    <DIR>          ..
02/02/2020  08:06    <DIR>          .atom

搜索模式 #

在上述所有模式中,执行的行数都会被保存到历史文件中,可以进行搜索。要在以前的历史记录中进行增量搜索,输入 ^R–控制键和 r 键。提示符将变为(reverse-i-search):,当你输入搜索查询时,搜索查询将出现在引号中。当你输入更多的内容时,与查询相匹配的最新结果会动态地更新到冒号的右边。如果要使用相同的查询找到一个较早的结果,只需再次输入 ^R

就像 ^R 是反向搜索一样,^S 是正向搜索,并提示(i-search):。两者可以相互结合使用,分别在上一个或下一个匹配结果中移动。

键绑定 #

Julia REPL 大量使用了键绑定。上面已经介绍了几个控制键绑定(^D 用于退出,^R^S 用于搜索),但还有更多。除了控制键,还有元键绑定。这些因平台不同而变化较大,但大多数终端默认使用 alt-option- 按住键发送元键(也可以配置成这样),或者按 Esc 键,然后按键。

Keybinding Description
Program control
^D 退出(当缓存为空时)
^C 中断或取消
^L 清理控制台屏幕
Return/Enter, ^J 新行,如果完成了就执行
meta-Return/Enter 插入新行而不执行
?; 进入帮助或shell模式(当在行的开头时)
^R, ^S 递增式历史检索,如上所述

自定义键绑定 #

Julia 的 REPL 键绑定可以通过向 REPL.setup_interface 传递一个字典来完全定制用户的偏好。这个字典的键可以是字符或字符串。键 ‘*’ 指的是默认操作。控制加字符x的绑定用"^x"表示。Meta 加x可以写成 “\M-x” 或 “\ex”,Control 加 x 可以写成 “\C-x” 或 “^x”。自定义 keymap 的值必须是 nothing(表示输入应该被忽略)或接受签名的函数(PromptState, AbstractREPL, Char)。REPL.setup_interface 函数必须在 REPL 初始化之前被调用,通过在 atreplinit 注册操作。例如,要绑定上下方向键来移动历史记录而不需要前缀搜索,可以在 ~/.julia/config/startup.jl 中放入以下代码。

import REPL
import REPL.LineEdit

const mykeys = Dict{Any,Any}(
    # Up Arrow
    "\e[A" => (s,o...)->(LineEdit.edit_move_up(s) || LineEdit.history_prev(s, LineEdit.mode(s).hist)),
    # Down Arrow
    "\e[B" => (s,o...)->(LineEdit.edit_move_down(s) || LineEdit.history_next(s, LineEdit.mode(s).hist))
)

function customize_keys(repl)
    repl.interface = REPL.setup_interface(repl; extra_repl_keymap = mykeys)
end

atreplinit(customize_keys)

用户应该参考 LineEdit.jl 来发现键输入的可用操作。

Tab 补全 #

在 REPL 的 Julian 和帮助模式下,可以输入函数或类型的前几个字符,然后按tab键,得到一个所有匹配的列表:

julia> stri[TAB]
stride     strides     string      strip

julia> Stri[TAB]
StridedArray    StridedMatrix    StridedVecOrMat  StridedVector    String

tab 键也可以用来用它们的 Unicode 等价物替代 LaTeX 数学符号,并获得 LaTeX 匹配列表。

julia> \pi[TAB]
julia> π
π = 3.1415926535897...

julia> e\_1[TAB] = [1,0]
julia> e₁ = [1,0]
2-element Array{Int64,1}:
 1
 0

julia> e\^1[TAB] = [1 0]
julia> e¹ = [1 0]
1×2 Array{Int64,2}:
 1  0

julia> \sqrt[TAB]2     # √ is equivalent to the sqrt function
julia> √2
1.4142135623730951

julia> \hbar[TAB](h) = h / 2\pi[TAB]
julia> ħ(h) = h / 2π
ħ (generic function with 1 method)

julia> \h[TAB]
\hat              \hermitconjmatrix  \hkswarow          \hrectangle
\hatapprox        \hexagon           \hookleftarrow     \hrectangleblack
\hbar             \hexagonblack      \hookrightarrow    \hslash
\heartsuit        \hksearow          \house             \hspace

julia> α="\alpha[TAB]"   # LaTeX completion also works in strings
julia> α="α"

完整的tab-补全列表可以在手册的 Unicode 输入部分找到。

路径补全适用于字符串和 julia 的 shell 模式:

julia> path="/[TAB]"
.dockerenv  .juliabox/   boot/        etc/         lib/         media/       opt/         root/        sbin/        sys/         usr/
.dockerinit bin/         dev/         home/        lib64/       mnt/         proc/        run/         srv/         tmp/         var/
shell> /[TAB]
.dockerenv  .juliabox/   boot/        etc/         lib/         media/       opt/         root/        sbin/        sys/         usr/
.dockerinit bin/         dev/         home/        lib64/       mnt/         proc/        run/         srv/         tmp/         var/

Tab 补全可以帮助调查匹配输入参数的可用方法。

julia> max([TAB] # All methods are displayed, not shown here due to size of the list

julia> max([1, 2], [TAB] # All methods where `Vector{Int}` matches as first argument
max(x, y) in Base at operators.jl:215
max(a, b, c, xs...) in Base at operators.jl:281

julia> max([1, 2], max(1, 2), [TAB] # All methods matching the arguments.
max(x, y) in Base at operators.jl:215
max(a, b, c, xs...) in Base at operators.jl:281

关键词也显示在 ; 后面的建议方法中,见下面一行,其中 limitkeepempty 是关键词参数:

julia> split("1 1 1", [TAB]
split(str::AbstractString; limit, keepempty) in Base at strings/util.jl:302
split(str::T, splitter; limit, keepempty) where T<:AbstractString in Base at strings/util.jl:277

方法的补全使用类型推断,因此即使参数是函数输出的,也能看到参数是否匹配。函数需要类型稳定,完成才能够删除不匹配的方法。

Tab 补全也可以帮助补全字段:

julia> import UUIDs

julia> UUIDs.uuid[TAB]
uuid1        uuid4         uuid_version

也可以补全函数输出的字段:

julia> split("","")[1].[TAB]
lastindex  offset  string

函数输出的字段完成采用类型推断,只有在函数类型稳定的情况下,它才能建议字段。

字典键也可以用 tab 补全:

julia> foo = Dict("qwer1"=>1, "qwer2"=>2, "asdf"=>3)
Dict{String,Int64} with 3 entries:
  "qwer2" => 2
  "asdf"  => 3
  "qwer1" => 1

julia> foo["q[TAB]

"qwer1" "qwer2"
julia> foo["qwer

自定义颜色 #

Julia 和 REPL 所使用的颜色也是可以自定义的。要改变 Julia 提示符的颜色,您可以在您的 ~/.julia/config/startup.jl 文件中添加以下内容,该文件应放在您的主目录中。

function customize_colors(repl)
    repl.prompt_color = Base.text_colors[:cyan]
end

atreplinit(customize_colors)

可用的颜色键可以通过在 REPL 的帮助模式下输入 Base.text_colors 来查看。此外,对于支持 256 色的终端来说,整数 0 到 255 可以用作颜色键。

也可以通过在上面的 customize_colors 函数中设置 repl 的相应字段(分别为 help_colorshell_colorinput_coloranswer_color)来改变帮助和 shell 提示符以及输入和回答文字的颜色。对于后两者,要确保 envcolors 字段也设置为 false。

也可以通过使用 Base.text_colors[:bold] 作为颜色来应用粗体格式。例如,要用粗体字打印答案,可以使用下面的 ~/.julia/config/startup.jl:

function customize_colors(repl)
    repl.envcolors = false
    repl.answer_color = Base.text_colors[:bold]
end

atreplinit(customize_colors)

你也可以通过设置适当的环境变量来定制用于渲染警告和信息消息的颜色。例如,要分别用洋红色、黄色和青色来渲染错误、警告和信息消息,你可以在 ~/.julia/config/startup.jl 文件中添加以下内容:

ENV["JULIA_ERROR_COLOR"] = :magenta
ENV["JULIA_WARN_COLOR"] = :yellow
ENV["JULIA_INFO_COLOR"] = :cyan

TerminalMenus #

TerminalMenus 是 Julia REPL 的一个子模块,可以在终端中实现小型、低配的交互式菜单。

例子 #

import REPL
using REPL.TerminalMenus

options = ["apple", "orange", "grape", "strawberry",
            "blueberry", "peach", "lemon", "lime"]

RadioMenu #

RadioMenu 允许用户从列表中选择一个选项。request 函数显示交互式菜单并返回所选选项的索引。如果用户按 ‘q’ 或 ctrl-crequest 将返回 -1

# `pagesize` is the number of items to be displayed at a time.
#  The UI will scroll if the number of options is greater
#   than the `pagesize`
menu = RadioMenu(options, pagesize=4)

# `request` displays the menu and returns the index after the
#   user has selected a choice
choice = request("Choose your favorite fruit:", menu)

if choice != -1
    println("Your favorite fruit is ", options[choice], "!")
else
    println("Menu canceled.")
end

输出:

Choose your favorite fruit:
^  grape
   strawberry
 > blueberry
v  peach
Your favorite fruit is blueberry!

MultiSelectMenu #

多重选择菜单(MultiSelectMenu)允许用户从一个列表中选择许多选择。

# here we use the default `pagesize` 10
menu = MultiSelectMenu(options)

# `request` returns a `Set` of selected indices
# if the menu us canceled (ctrl-c or q), return an empty set
choices = request("Select the fruits you like:", menu)

if length(choices) > 0
    println("You like the following fruits:")
    for i in choices
        println("  - ", options[i])
    end
else
    println("Menu canceled.")
end

输出:

Select the fruits you like:
[press: d=done, a=all, n=none]
   [ ] apple
 > [X] orange
   [X] grape
   [ ] strawberry
   [ ] blueberry
   [X] peach
   [ ] lemon
   [ ] lime
You like the following fruits:
  - orange
  - grape
  - peach

Customization / Configuration #

所有的界面定制都是通过关键字 TerminalMenus.config() 函数完成的。

参数 #
  • charset::Symbol=:na: 要使用的ui字符(:ascii:unicode); 被其他参数覆盖。
  • cursor::Char='>'|'→': 光标使用的字符。
  • up_arrow::Char='^'|'↑': 用于向上箭头的字符。
  • down_arrow::Char='v'|'↓': 用于向下箭头的字符。
  • checked::String="[X]"|"✓":用于检查的字符串。
  • unchecked::String="[]"|"⬚"):用于未选中的字符串。
  • scroll::Symbol=:na: 如果 :wrap,则将光标环绕在顶部和底部,如果 :nowrap 则不环绕光标。
  • supress_output::Bool=false。用于测试。如果为真,菜单不会被打印到控制台。
  • ctrl_c_interrupt::Bool=true: 如果为假,在 ^C 时返回空,如果为真,在 ^C 时抛出 InterruptException()。

例子 #

julia> menu = MultiSelectMenu(options, pagesize=5);

julia> request(menu) # ASCII is used by default
[press: d=done, a=all, n=none]
   [ ] apple
   [X] orange
   [ ] grape
 > [X] strawberry
v  [ ] blueberry
Set([4, 2])

julia> TerminalMenus.config(charset=:unicode)

julia> request(menu)
[press: d=done, a=all, n=none]
   ⬚ apple
   ✓ orange
   ⬚ grape
 → ✓ strawberry
↓  ⬚ blueberry
Set([4, 2])

julia> TerminalMenus.config(checked="YEP!", unchecked="NOPE", cursor='⧐')

julia> request(menu)
[press: d=done, a=all, n=none]
   NOPE apple
   YEP! orange
   NOPE grape
 ⧐ YEP! strawberry
↓  NOPE blueberry
Set([4, 2])

参考 #

Base.atreplinit

atreplinit(f)

注册一个单参数函数,在交互式会话中,在 REPL 接口初始化之前被调用;这对自定义接口很有用。f 的参数是 REPL 对象。这个函数应该在 ~/.julia/config/startup.jl 初始化文件中调用。