Introducing Julia/print

Introduction

Julia是最近来到编程语言世界的，她还没有积累大量的介绍性文本。这种情况肯定会改变。很快我们就会看到：

• Julia in 24 hours
• Learn Julia the Hard Way
• Julia for Dummies
• Julia for Fun and Profit

Wikibooks 的优点和缺点，除了自由和开放之外，是任何人都可以在任何时候编辑任何东西。理论上，随着更多的人添加改进和更正，wikibook 只能变得更好。在实践中，wikibook 可能会失去焦点和一致性，因为它获得了准确性和覆盖范围。但是，因为 Julia 社区已经建立了一种鼓励参与语言发展的良好风气，所以这本维基百科全书是可以自由编辑的，这是正确的，因为每个人都可以自由地编辑这本wikibook。

 « Introducing Julia新手入门 » 目录 REPL 环境

新手入门

在 macOS X 上

$julia _ _ _ _(_)_ | Documentation: https://docs.julialang.org (_) | (_) (_) | _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help. | | | | | | |/ _ | | | | |_| | | | (_| | | Version 1.0.0 (2018-08-08) _/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release |__/ | julia> 或者，您可以在终端中键入如下内容： $ /Applications/Julia-1.0.app/Contents/Resources/julia/bin/julia


$ls /Applications/Julia*/Contents/Resources/julia/bin/julia /Applications/Julia-0.4.5.app/Contents/Resources/julia/bin/julia /Applications/Julia-0.4.7.app/Contents/Resources/julia/bin/julia /Applications/Julia-0.5.app/Contents/Resources/julia/bin/julia /Applications/Julia-0.6.app/Contents/Resources/julia/bin/julia /Applications/Julia-0.7.app/Contents/Resources/julia/bin/julia /Applications/Julia-1.0.app/Contents/Resources/julia/bin/julia  直接在终端上运行 通常, Julia 安装在 /Applications, 这并不包含在你的 PATH 变量中, 因此如果你在命令行中键入 julia 的话，shell 并不能找到它。 那么要怎么做才能使我们在终端中键入 julia 后能直接打开 julia 呢？下面是一些可选的方法： 第一种方法：在找到Julia二进制可执行文件的位置(请参见上文)后，可以定义以下别名： alias julia="/Applications/Julia-1.0.app/Contents/Resources/julia/bin/julia"  显然，每次版本号更改时，都必须对其进行更新。 作为一种替代方法，您可以将 /Applications/Julia... 添加到你的 PATH 变量中(操作系统从该变量所指定的路径中查找可执行程序)： PATH="/Applications/Julia-1.0.app/Contents/Resources/julia/bin/:${PATH}"
export PATH


ln -fs "/Applications/Julia-1.0.app/Contents/Resources/julia/bin/julia" /usr/local/bin/julia

#!/usr/bin/env julia

运行 Julia 程序

$julia hello-world.jl  或者在 Julia REPL 内部运行该代码： $ julia
julia> include("hello-world.jl")


#!/Applications/Julia-1.0.app/Contents/Resources/julia/bin/julia


#!/usr/bin/env julia






Linux

$sudo dnf remove julia 请注意：以上命令仅适用于 Fedora，其他下游的发行版如：RHEL 和 CentOS，请核查他们的发行版，看是否有 julia 的安装包。 使用二进制文件 您可以直接从二进制文件中使用 Julia，而无需在您的计算机上安装它。如果您使用的是旧的Linux发行版，或者您没有管理员对机器的访问权限，那么这是非常有用的。 只需从网站下载二进制文件，解压缩到一个目录。在此目录中进入 bin 文件夹然后运行： $ ./julia

$chmod +x julia  基本上，这种方法可以在任何Linux发行版上使用。 通过 Julia 运行脚本 要告诉您的操作系统应该使用 Julia 运行脚本，您可以用叫做 shebang 的语法。要执行此操作，只需在脚本的最顶部使用以下行： #!/usr/bin/env julia 以此作为脚本的第一行，操作系统将在 path 中搜索“Julia”，并使用它运行脚本。 The REPL  « Introducing JuliaREPL 环境 » 新手入门 数组和元组 REPL 环境 默认情况下，键入 julia 将启动一个交互式 REPL，即读取/求值/打印/循环。它允许您在 Julia 代码中键入表达式，并立即在屏幕上看到计算结果。 • Reads what you type (读取你输入的内容) • Evaluates it (求值) • Prints out the return value, then （打印出返回值，然后） • Loops back and does it all over again （返回并周而复始） REPL 是一个开始尝试这种语言的好地方。但是对于正式代码编写，文本编辑器或交互式笔记本环境 (例如 IJulia/Jupyter) 才是更好的选择。 REPL 的优势在于：它很简单，不需要任何安装或配置就可以工作，还有一个内置的帮助系统。通过它你可以快速的验证一些想法。总而言之，它很适合用于学习 Julia. 使用 REPL 键入一些 Julia 代码，然后键入 Return/Enter。Julia 将计算您键入的内容并返回结果： julia> 42 <Return/Enter> 42 julia>  如果您使用的是 Jupyter (IPython) notebook，则可能必须键入 Control-Enter 或 Shift-Enter. 如果不希望看到打印表达式的结果，请在表达式末尾使用分号： julia> 42; julia>  此外，如果要访问在 REPL 上键入的最后一个表达式的值，它将存储在变量 ans 中： julia> ans 42  如果未完成第一行的表达式，请继续键入，直到完成为止。例如： julia> 2 + <Return/Enter>  Julia 会有耐心地等待着，直到你结束这个表达式： 2 <Return/Enter>  于是乎你就能看到答案： 4 julia>  帮助和查找帮助 键入问号 ? julia> ?  您将立即切换到帮助模式，(在终端中)提示符将变为黄色： help?>  现在，您可以键入某项的名称(函数名称应不带括号)： help?> exit search: exit atexit textwidth process_exited method_exists indexin nextind IndexLinear TextDisplay istextmime exit(code=0) Stop the program with an exit code. The default exit code is zero, indicating that the program completed successfully. In an interactive session, exit() can be called with the keyboard shortcut ^D. julia>  请注意，帮助系统已尝试查找与您键入的字母匹配的所有单词，并向您显示它找到的内容。 如果要搜索文档，可以使用 apropos 和一个搜索的字符串： julia> apropos("determinant") LinearAlgebra.det LinearAlgebra.logabsdet LinearAlgebra.logdet  您将看到一个函数列表，其名称或描述包含字符串。 julia> apropos("natural log") Base.log Base.log1p help?> log search: log log2 log1p log10 logging logspace Clong Clonglong Culong Culonglong task_local_storage log(b,x) Compute the base b logarithm of x. Throws DomainError for negative Real arguments.  诸如此类。 Shell 模式 如果键入分号 julia> ;  立即切换到 shell 模式： shell>  (提示变为红色)。在shell模式下，您可以键入任何shell(即非Julia)命令并查看结果： shell> ls file.txt executable.exe directory file2.txt julia>  然后提示符切换回julia，因此每次想执行shell命令时，都必须键入分号。该模式下可用的命令是系统 shell 的命令。 包管理模式 如果键入右方括号作为第一个字符： julia> ]  立即切换到 package 模式： v1.0 pkg>  这是您执行包管理任务的地方，例如添加包、测试包等等。 要离开 package 模式，请在空行上按 Backspace 或 CTRL+C。 内置函数和宏 以下是在REPL提示中提供的一些有用的交互函数和宏： • varinfo() – 打印有关 module 中导出的全局变量的信息。 julia> varinfo() name size summary –––––––––––––––– ––––––––––– ––––––––––– Base Module Core Module InteractiveUtils 222.893 KiB Module Main Module ans 1.285 KiB Markdown.MD  • @which – 告诉您将为函数和特定参数调用具体哪个方法： julia> @which sin(3) sin(x::Real) in Base.Math at special/trig.jl:53  • versioninfo() – 获取 Julia 版本和平台信息： julia> versioninfo() Julia Version 1.0.0-rc1.0 Commit f92a55a06a (2018-08-07 16:29 UTC) Platform Info: OS: macOS (x86_64-apple-darwin14.5.0) CPU: Intel(R) Core(TM) i5-7500 CPU @ 3.40GHz WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-6.0.0 (ORCJIT, skylake) Environment: JULIA_EDITOR = mvim  还有一种快速查找版本的方法： julia> VERSION v"1.0.0"  • edit("pathname") – 启动默认编辑器并打开文件pathname进行编辑 • @edit rand() – 启动默认编辑器并打开包含定义了内置函数 rand() 的文件 • less("filename-in-current-directory") – 在当前位置显示文件 • clipboard("stuff") – 复制 "stuff" 到系统剪贴板 • clipboard() – 将剪贴板的内容粘贴到当前 REPL 行中 • dump(x) – 在屏幕上显示有关Julia对象 x 的信息 • names(x) – 获取 module x所以导出的名称的数组。 • fieldnames(typeof(x)) – 获取属于类型为 x 的符号的数据字段的数组。 <TAB> 键: 自动补全 TAB 键通常能够完成或建议完成您键入其名称的某项内容。例如，如果我键入w，然后按Tab键(在有多个选项时按两次)，则会列出以“w”开头的所有当前可用函数： julia> w <TAB> wait walkdir which while widemul widen withenv write  这既适用于 Julia 实例，也适用于 shell 和 package 模式。例如，下面是如何从Julia内部导航到某个目录： shell> cd ~ /Users/me shell> cd Doc <TAB> shell> cd Documents/ shell> ls ...  请记住，您可以使用 ? 并键入其全名(或使用 TAB-补全)来获得有关函数的帮助。 TAB-补全也适用于unicode符号：例如键入 \alp 然后按 TAB 将会得到 \alpha 再按一次 TAB 就会得到 α 对于Emoji： 键入 \:fe 然后按 TAB 将会得到 \:ferris_wheel: 再按一次 TAB 就会得到 🎡。 历史记录 您可以使用上箭头键和下箭头键回顾以前的命令记录(您可以退出并重新启动，而不会删除该历史记录)。 因此，您不必再次键入长的多行表达式，因为您可以从历史中回忆起它。如果您键入了大量表达式，则可以通过按Ctrl-R和Ctrl-S来前后搜索它们。 变量作用域与性能 一个关于 REPL 的警告。REPL 在 Julia 的 全局范围 内运行。 通常，当编写较长的代码时，您会将代码放在函数中，并将函数组织到模块和包中。当您的代码被组织成函数时，Julia的编译器工作得更有效，因此您的代码将运行得更快。 还有一些事情在 top level 是做不到的：比如为变量的值指定类型。 更改提示符并自定义 Julia 会话 每次启动Julia时，都会运行以下 Julia 文件(除非使用startup-file=no选项)。 ~/.julia/config/startup.jl  这使您可以加载任何您所知的以后需要的包。例如，如果要自动定制 REPL 会话，可以安装能在启动文件中自定义REPL的外观和行为的包 OhMyREPL.jl (https://github.com/KristofferC/OhMyREPL.jl) ，然后在 startup 文件中： using OhMyREPL  如果您只想在每次启动 Julia 会话时设置提示，则只需添加以下说明： using REPL function myrepl(repl) repl.interface = REPL.setup_interface(repl) repl.interface.modes[1].prompt = "julia-$(VERSION.major).$(VERSION.minor)> " return end atreplinit(myrepl)  这只会设置当前的REPL提示符，以显示您的会话正在使用的 Julia 版本号。 Julia 与 数学 你把 Julia REPL 当作一个强大的计算器。这也是很好的练习。(这是介绍交互式编程语言的传统，也是认识该语言的好方法。) 输入长数字 世界上一半的人使用逗号(,)将长数字分成三组，另一半使用句点(.)。(我们其余的人使用科学记法)。 在Julia中，可以使用下划线(_)分隔： julia> 1_000_000 - 2_015 997985  尽管你在返回的结果中看不到。 要使用科学符号，只需键入“e”，并记住不要使用空格： julia> planck_length = 1.61619997e-34  我的电脑有多快？（每秒执行多少个十亿次的浮点运算） julia> peakflops() / 1e9 48.778354495441356 julia> peakflops() / 1e9 54.20509453559899  (请注意第二次速度更快，这是因为 Julia 的编译起作用了。) 操作符即函数 julia> 2 + 2 4 julia> 2 + 3 + 4 9  数字相加的等效形式： julia> +(2, 2) 4  通常在值之间使用的运算符是普通的 Julia 函数，可以与其他函数用相同的方式使用。类似的： julia> 2 + 3 + 4 9  可以写成 julia> +(2, 3, 4) 9  以及 julia> 2 * 3 * 4 24  可以写成 julia> *(2,3,4) 24  Julia 还提供了一些数学常量： julia> pi π = 3.1415926535897...  你可以在 MathConstants 模块上找到一些其他的数学常量： julia> Base.MathConstants.golden φ = 1.6180339887498... julia> Base.MathConstants.e e = 2.7182818284590...  所有常用操作符均可用： julia> 2 + 3 - 4 * 5 / 6 % 7 1.6666666666666665  注意运算符的优先级。在这种情况下，它是： ((2 + 3) - ((4 * 5) / 6) % 7)  如果要检查运算符的优先级，请将表达式包含在 :() 中:  julia> :(2 + 3 - 4 * 5 / 6 % 7) :((2 + 3) - ((4 * 5) / 6) % 7)  (有关这方面的更多信息，请参阅本书的元编程章节). 乘法通常是写成 * 的，但当将变量乘以数字文字时，可以省略这一点： julia> x = 2 2 julia> 2x + 1 5  julia> 10x + 4x - 3x/2 + 1 26.0  这使得方程式更容易写。 有时需要括号来控制计算顺序： julia> (1 + sqrt(5)) / 2 1.618033988749895  其他一些需要注意的事项包括： • ^ 乘方 • % 余数 要生成有理数，请使用两个斜杠(//)： julia> x = 666//999 2//3  也有反除法"\"，所以 x/y = y\x. 标准算术运算符还具有特殊的更新版本，您可以使用该版本快速更新变量： • += • -= • *= • /= • \= • %= • ^= 例如，在定义了变量 x 之后: julia> x = 5 5  你可以给它加 2 julia> x += 2 7  然后乘以 100 julia> x *= 100 700  再然后把它减到它模11的值： julia> x %= 11 7  有对数组起作用的元素级运算符。这意味着可以将两个数组元素乘以元素： julia> [2,4] .* [10, 20] 2-element Array{Int64,1}: 20 80  数组是 Julia 的基础，在这本书中也有他们自己的章节。 如果对两个整数用 / 进行除法运算，则答案始终是一个浮点数。如果您使用过 Python 2 版本，您会记得 Python 返回一个整数结果。Python 3 现在返回一个浮点数。 Julia提供了一个整数除法运算 ÷ (键入 \div TAB ，或使用函数版本的 div() 。当您希望得到整数结果而不是用 / 返回的浮点时，应使用此选项。 julia> 3 ÷ 2 1 julia> div(3, 2) 1  整型数溢出 如果您认为您的计算将突破64位限制，请通过使用 big 函数将操作数存储为大数来选择“大整数”： julia> 2^64 # oops 0 julia> big(2)^64 # better 18446744073709551616 julia> 2^big(64) # equally better 18446744073709551616  要为Julia程序获得最快的执行速度，您应该了解如何在不引入“类型不稳定”的情况下存储数据和变量。 进制转换 当使用 REPL 作为计算器时，这些方便的实用函数可能会很有用。 bitstring() 函数显示数字的文字二进制表示形式，存储如下： julia> bitstring(20.0) "0100000000110100000000000000000000000000000000000000000000000000" julia> bitstring(20) "0000000000000000000000000000000000000000000000000000000000010100"  请注意，正如您所预期的，浮点“版本”的存储方式是不同的。 要从二进制字符串返回十进制，您可以使用 parse()，它接受目标类型和数字基数： julia> parse(Int, "0000011", base=2) 3 julia> parse(Int, "DecaffBad", base=16) 59805531053  若要使用默认10以外的数字基，请使用 string 函数将整数转换为字符串： julia> string(65535, base=16) "ffff" julia> string(64, base=8) "100"  相反， digits(number, base=b) 返回给定基数的数字位的数组 ： julia> digits(255, base=16) 2-element Array{Int64,1}: 15 15  变量 在这个表达式中： julia> x = 3  x 是一个变量，是数据对象的命名存储位置。在 Julia 中，虽然变量名不能以数字或标点符号开头，但可以按照您的喜好来命名变量。如果需要，可以使用Unicode字符。 若要赋值，请使用单个等号。 julia> a = 1 1 julia> b = 2 2 julia> c = 3 3  要测试相等性，您应该使用 == 运算符或 isequal() 函数。 在 Julia 中，可以同时给多个变量赋值： julia> a, b = 5, 3 (5,3)  请注意，此表达式的返回值是一个圆括号内的、逗号分隔的、有序的元素列表：简称元组 julia> a 5 julia> b 3  数字和变量相乘 值得重复的是，您可以在变量名称前面加上一个数字来将其相乘，而不必使用星号(*)。例如： julia> x = 42 42 julia> 2x 84 julia> .5x 21.0 julia> 2pi 6.283185307179586  特殊符号 JuliaREPL提供了对特殊字符的简单访问，例如希腊字母字符、下标和特殊的数学符号。如果键入反斜杠，则可以键入字符串(通常是等效的LaTeX字符串)以插入相应的字符。例如，如果键入以下内容： julia> \sqrt <TAB>  Julia 会将 \sqrt 替换为平方根符号： julia> √  其他一些例子：  \Gamma Γ \mercury ☿ \degree ° \cdot ⋅ \in ∈ 在Julia源代码中有一个完整的列表。作为一般原则，在 Julia 中鼓励您查看源代码，因此有一些有用的内置函数可用于查看Julia源文件。例如，在MacOS上，这些符号存储在： julia> less("/Applications/Julia-1.0.app/Contents/Resources/julia/share/julia/stdlib/v1.0/REPL/src/latex_symbols.jl")  less 通过 pager 运行文件(即 Unix 中的 less命令)如果您很勇敢，请尝试使用 edit() 而不是 less(). 这将启动一个编辑器并打开文件。 还可以在REPL中使用Emoji和其他Unicode字符。 对于emoji，键入Emoji字符名称，在冒号之间，在反斜杠后面，然后按<TAB>： julia> \:id: <TAB>  会更改为： julia> 🆔  输入不在此列表中的Unicode符号是可能的，但更依赖于操作系统：在MacOS上，在键入Unicode十六进制数字时按住 Ctrl/Alt键(启用Unicode十六进制键盘)；在Windows上，输入Ctrl+Shift+u，后跟十六进制数字。) julia> ✎ = 3 3 julia> ✎ 3  数学函数 因为Julia特别适合于科学和技术计算，所以您可以立即使用许多数学函数，而且您通常不必导入它们或使用前缀，它们已经可用了。 三角函数值要求以弧度为单位： julia> sin(pi / 2) 1.0  但是也有基于度的版本：sind(90) 找到90度的正弦。使用 deg2rad()rad2deg() 在度数和弧度之间进行转换。 还有许多对数函数： julia> log(12) 2.4849066497880004  和精确的勾股定理函数 hypot() julia> hypot(3, 4) 5.0  norm() 函数返回向量的 "p" 范数或矩阵的算子范数。下面是 divrem(): julia> divrem(13, 3) # returns the division and the remainder (4,1)  There are dozens of others. 有一个称为ans 的系统范围的变量，它会记住最近的结果，因此您可以在下一个表达式中使用它。 julia> 1 * 2 * 3 * 4 * 5 120 julia> ans/10 12.0  小练习 猜猜看，然后使用帮助系统了解 mod2pi()isapprox() 做了什么。 这里描述了作为Julia标准提供的所有函数：[1] 随机数 rand() – 获取一个介于0和1之间的随机 Float64 数值。 julia> rand() 0.11258244478647295  rand(2, 2) – 维数为 2, 2 类型为 Float64 的数组 rand(type, 2, 2) – 维数为 2, 2 类型为 type 的数组 rand(range, dims) – 具有指定的范围内(包括两端)的数字数组： julia> rand(0:10, 6) 6-element Array{Int64,1}: 6 7 9 6 3 10  (有关范围对象的详细信息，请参见数组一章。) The rand() function can generate a true or false value if you tell it to, by passing the Bool keyword: 如果传递 Bool 关键字给 rand() 函数，它可以生成 true 或 false 值： julia> rand(Bool) false  或是一堆 true 和 false ： julia> rand(Bool, 20) 20-element Array{Bool,1}: false true false false false true true false false false false false false false true true false true true false  服从分布的随机数 randn() 给出正态分布中的一个随机数，平均值为0，标准差为1。randn(n) 给出 n 个这样的数字： julia> randn() 0.8060073309441075 julia> randn(10), ([1.31598,1.55126,-1.14605,-0.562148,0.69725,0.468769,-1.58275,0.238471,2.72857,1.11561],)  ( randn(10) 后面的逗号仅用于行的可视化) 如果已安装 Plots 包，则可以打印以下内容： julia> using Plots; gr()  julia> histogram(randn(10000), nbins=100)  设置随机数种子 Random 包 涵盖了更多的随机函数，如 randperm(), shuffle()seed!.。 在使用随机数之前，可以为随机数生成器设定特定值。这确保后续的随机数将遵循相同的序列，如果它们从相同的种子开始。可以使用 seed!()MersenneTwister() 函数为生成器设定种子。 添加 Random 包后，可以执行以下操作： julia> using Random julia> Random.seed!(10); julia> rand(0:10, 6) 6-element Array{Int64,1}: 6 5 9 1 1 0  julia> rand(0:10, 6) 6-element Array{Int64,1}: 10 3 6 8 0 1  在重新启动Julia后，相同的种子保证相同的随机数。 简单的输入示例 下面是一个简单的示例，说明如何编写和运行从键盘读取输入的函数： julia> function areaofcircle() println("What's the radius?") r = parse(Float64, readline(stdin)) println("a circle with radius$r has an area of:")
println(π * r^2)
end
areaofcircle (generic function with 1 method)

julia> areaofcircle()
42
a circle with radius 42.0 has an area of:
5541.769440932395
julia>

 « Introducing JuliaREPL 环境 » 新手入门 数组和元组

Arrays and Tuples

 « Introducing Julia数组和元组 » REPL 环境 类型

创建数组

创建简单数组

julia> a = [1, 2, 3, 4, 5]
5-element Array{Int64,1}:
1
2
3
4
5


Julia 通知您 ("5-element Array{Int64,1}") 表示您创建了一个有 5 个元素（每个元素都是 64 位整数）的一维数组。并将变量 a 绑定到该数组。 请注意，这个过程很智能：例如，如果其中一个元素看起来像一个浮点数，您将得到 Float64 的数组：

julia> a1 = [1, 2, 3.0, 4, 5]
5-element Array{Float64,1}:
1.0
2.0
3.0
4.0
5.0


julia> s = ["this", "is", "an", "array", "of", "strings"]
6-element Array{String,1}:
"this"
"is"
"an"
"array"
"of"
"strings"


julia> trigfuns = [sin, cos, tan]
3-element Array{Function,1}:
sin
cos
tan



未初始化的

julia> array = Array{Int64}(undef, 5)
5-element Array{Int64,1}:
4520632328
4614616448
4520668544
4520632328
4615451376
4294967362

julia> array3 = Array{Int64}(undef, 2, 2, 2)
2×2×2 Array{Int64,3}:
[:, :, 1] =
4452254272  4452255728
4452256400  4456808080

[:, :, 2] =
4456808816  4452255728
4456808816  4452254272


包含任意类型元素的数组

julia> [1, "2", 3.0, sin, pi]
5-element Array{Any, 1}:
1
"2"
3.0
sin
π = 3.1415926535897...


julia> typeof(ans)
Array{Any,1}


空数组

julia> Int64[1, 2, 3, 4]
4-element Array{Int64,1}:
1
2
3
4


julia> Int64[1, 2, 3, 4, 5, 6, 7, 8,  9, 10.1]
ERROR: InexactError()


julia> b = Int64[]
0-element Array{Int64,1}

julia> b = String[]
0-element Array{String,1}

julia> b = Float64[]
0-element Array{Float64,1}


创建二维数组和矩阵

julia> [1 2 3 4]
1x4 Array{Int64,2}:
1  2  3  4


julia> [1 2 3 4 ; 5 6 7 8]
2x4 Array{Int64,2}:
1  2  3  4
5  6  7  8


行向量和列向量

julia> [1, 2, 3, 4, 5]
5-element Array{Int64,1}:
1
2
3
4
5


julia> [1 2 3 4 5]
1x5 Array{Int64,2}:
1  2  3  4  5


julia> [1 2 3; 4 5 6]
2x3 Array{Int64,2}:
1  2  3
4  5  6


julia> x = rand(5)
5-element Array{Float64,1}:
0.4821773161183929
0.5811789456966778
0.7852806713801641
0.23626682918327369
0.6777187748570226

julia> x = rand(5, 1)
5×1 Array{Float64,2}:
0.0723474801859294
0.6314375868614579
0.21065681560040828
0.8300724654838343
0.42988769728089804


Julia 提供 Vector and Matrix 构造函数 。不过这些只是未初始化的一维数组和二维数组的别名：

julia> Vector(undef, 5)
5-element Array{Any,1}:
#undef
#undef
#undef
#undef
#undef

julia> Matrix(undef, 5, 5)
5x5 Array{Any,2}:
#undef  #undef  #undef  #undef  #undef
#undef  #undef  #undef  #undef  #undef
#undef  #undef  #undef  #undef  #undef
#undef  #undef  #undef  #undef  #undef
#undef  #undef  #undef  #undef  #undef


使用范围对象 创建数组

julia> 1:10
1:10


julia> for n in 1:10 print(n) end
12345678910


julia> collect(1:10)
10-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10


julia> collect(3.5:9.5)
7-element Array{Float64,1}:
3.5
4.5
5.5
6.5
7.5
8.5
9.5



julia> collect(0:10:100)
11-element Array{Int64,1}:
0
10
20
30
40
50
60
70
80
90
100


julia> collect(4:-1:1)
4-element Array{Int64,1}:
4
3
2
1


julia> [1:6...]
6-element Array{Int64,1}:
1
2
3
4
5
6


(省略号运算符 ... 有时候被叫做 splat 运算符，它代表一系列参数)

范围 进一步使用

julia> range(1, length=12, stop=100)
1.0:9.0:100.0


julia> range(1, stop=100, step=10)
1:10:91


julia> collect(range(1, length=12, stop=100))
12-element Array{Float64,1}:
1.0
10.0
19.0
28.0
37.0
46.0
55.0
64.0
73.0
82.0
91.0
100.0


julia> exp10.(range(2.0, stop=3.0, length=5))
5-element Array{Float64,1}:
100.0
177.82794100389228
316.22776601683796
562.341325190349
1000.0


julia> step(range(1, length=10, stop=100))
11.0


julia> range(1, step=3, length=20) |> collect
20-element Array{Int64,1}:
1
4
7
10
13
16
19
22
25
28
31
34
37
40
43
46
49
52
55
58


对 Range 中的值 collect

julia> collect(0:5:100)
21-element Array{Int64,1}:
0
5
10
15
20
25
30
35
40
45
50
55
60
65
70
75
80
85
90
95
100



for i in collect(1:6)
println(i)
end

 1
2
3
4
5
6

for i in 1:6
println(i)
end

 1
2
3
4
5
6

使用推导和生成器创建数组

julia> [n^2 for n in 1:5]
5-element Array{Int64,1}:
1
4
9
16
25


julia> [r * c for r in 1:5, c in 1:5]
5x5 Array{Int64,2}:
1   2   3   4   5
2   4   6   8  10
3   6   9  12  15
4   8  12  16  20
5  10  15  20  25


julia> [i^2 for i=1:10  if i != 5]
9-element Array{Int64,1}:
1
4
9
16
36
49
64
81
100


julia> collect(x^2 for x in 1:10)
10-element Array{Int64,1}:
1
4
9
16
25
36
49
64
81
100

julia> collect(x^2 for x in 1:10 if x != 1)
9-element Array{Int64,1}:
4
9
16
25
36
49
64
81
100


创建和填满一个数组

- zeros(m, n) 创建具有m行和n列的零数组/矩阵：

julia> zeros(2, 3)
2x3 Array{Float64,2}:
0.0  0.0  0.0
0.0  0.0  0.0


julia> zeros(Int64, 3, 5)
3×5 Array{Int64,2}:
0  0  0  0  0
0  0  0  0  0
0  0  0  0  0


- ones(m, n) 创建一个全为1的 m 行 n列的数组或矩阵

julia> ones(2, 3)
2x3 Array{Float64,2}:
1.0  1.0  1.0
1.0  1.0  1.0


- rand(m, n) 创建全是随机数的 m 行 n 列的矩阵:

julia> rand(2, 3)
2×3 Array{Float64,2}:
0.488552   0.657078   0.895564
0.0190633  0.0120305  0.772106


- rand(range, m, n) 创建给定范围的随机数矩阵：

julia> rand(1:6, 3, 3)
3x3 Array{Int64,2}:
4  4  1
3  2  3
6  3  3


- randn(m, n) 创建 m 行 n 列矩阵。矩阵中充满均值为0，标准差为1的正态分布随机数。

trues()falses() 函数用布尔值 true 或 false 填充数组：

julia> trues(3, 4)
3x4 BitArray{2}:
true  true  true  true
true  true  true  true
true  true  true  true


julia> fill(42, 9)
9-element Array{Int64,1}:
42
42
42
42
42
42
42
42
42

julia> fill("hi", 2, 2)
2x2 Array{String,2}:
"hi"  "hi"
"hi"  "hi"


fill!() 有一个感叹号标记 (! , 或者叫"bang") 警告您即将更改现有数组的内容。（这在 Julia 中是一种有效的指示）。

julia> a = zeros(10)
10-element Array{Float64,1}:
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0

julia> fill!(a, 42)
10-element Array{Float64,1}:
42.0
42.0
42.0
42.0
42.0
42.0
42.0
42.0
42.0
42.0


julia> trueArray = falses(3,3)
3x3 BitArray{2}:
false  false  false
false  false  false
false  false  false

julia> fill!(trueArray, true)
3x3 BitArray{2}:
true  true  true
true  true  true
true  true  true

julia> trueArray
3x3 BitArray{2}:
true  true  true
true  true  true
true  true  true



julia> a = reshape(range(0, stop=100, length=30), 10, 3)
10×3 reshape(::StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}, 10, 3) with eltype Float64:
0.0      34.4828   68.9655
3.44828  37.931    72.4138
6.89655  41.3793   75.8621
10.3448   44.8276   79.3103
13.7931   48.2759   82.7586
17.2414   51.7241   86.2069
20.6897   55.1724   89.6552
24.1379   58.6207   93.1034
27.5862   62.069    96.5517
31.0345   65.5172  100.0


重复元素以填充数组

repeat() 是通过重复较小的数组来创建数组的函数。

julia> repeat([1, 2, 3], 2)
6-element Array{Int64,1}:
1
2
3
1
2
3

julia> repeat([1 2 3], 2)
2x3 Array{Int64,2}:
1  2  3
1  2  3


julia> repeat([1, 2, 3], 2, 3)
6x3 Array{Int64,2}:
1  1  1
2  2  2
3  3  3
1  1  1
2  2  2
3  3  3

julia> repeat([1 2 3], 2, 3)
2x9 Array{Int64,2}:
1  2  3  1  2  3  1  2  3
1  2  3  1  2  3  1  2  3


repeat() 函数还允许您通过复制源数组的行和列来创建数组。“内部”和“外部”选项确定是否重复行和/或列。例如，INNER=[2，3]生成一个数组，其中每行有两个副本，每列有三个副本：

innerouter 选项 决定是否重复行 和/或 列。例如, inner = [2, 3] 生成一个数组，其中行拷贝两份，列拷贝三分：

julia> repeat([1, 2], inner = [2, 3])
4x3 Array{Int64,2}:
1  1  1
1  1  1
2  2  2
2  2  2


julia> repeat([1, 2], outer = [2, 3])
4x3 Array{Int64,2}:
1  1  1
2  2  2
1  1  1
2  2  2


 julia> repeat([1 2; 3 4], inner=(2, 1), outer=(1, 3))
4×6 Array{Int64,2}:
1  2  1  2  1  2
1  2  1  2  1  2
3  4  3  4  3  4
3  4  3  4  3  4


Array 构造器

julia> Array{Int64}(undef, 6)
6-element Array{Int64,1}:
4454517776
4454517808
4454517840
4454517872
4454943824
4455998977


嵌套数组

   julia> a = Array[[1, 2], [3,4]]
2-element Array{Array,1}:
[1, 2]
[3, 4]


Array 构造器 也可以构建 数组的数组：

julia> Array[1:3, 4:6]
2-element Array{Array,1}:
[1,2,3]
[4,5,6]



julia> reshape([1, 2, 3, 4, 5, 6, 7, 8], 2, 4)
2x4 Array{Int64,2}:
1  3  5  7
2  4  6  8



julia> Array{String}(undef, 2, 3, 4)
2x3x4 Array{String,3}:
[:, :, 1] =
#undef  #undef  #undef
#undef  #undef  #undef

[:, :, 2] =
#undef  #undef  #undef
#undef  #undef  #undef

[:, :, 3] =
#undef  #undef  #undef
#undef  #undef  #undef

[:, :, 4] =
#undef  #undef  #undef
#undef  #undef  #undef



push!() 函数将另一项推送到数组的最后：

julia> push!(a, rand(1:100, 5))
3-element Array{Array,1}:
[1, 2]
[3, 4]
[4, 71, 82, 60, 48]

julia> push!(a, rand(1:100, 5))
4-element Array{Array,1}:
[1,2]
[3,4]
[4, 71, 82, 60, 48]
[4, 22, 52, 5, 14]


julia> a = Array{Int}[]
0-element Array{Array{Int64,N} where N,1}

julia> push!(a, [1, 2, 3])
1-element Array{Array{Int64,N} where N,1}:
[1, 2, 3]

julia> push!(a, [4, 5, 6])
2-element Array{Array{Int64,N} where N,1}:
[1, 2, 3]
[4, 5, 6]


julia> a = Vector{Int}[[1, 2], [3, 4]]
2-element Array{Array{Int64,1},1}:
[1, 2]
[3, 4]

julia> push!(a,  rand(1:100, 5))
3-element Array{Array{Int64, 1},1}:
[1, 2]
[3, 4]
[12, 65, 53, 1, 82]

julia> a[2]
2-element Array{Int64,1}:
3
4

julia> a[2][1]
3


复制数组

julia> a = collect(1:10); # hide the output with the semicolon

julia> b = similar(a)
10-element Array{Int64,1}:
4482975872
4482975792
1
4482975952
4482976032
4482976112
3
3
2
4520636161



julia> c = similar(b, String, (2, 2))
2x2 Array{String,2}:
#undef  #undef
#undef  #undef


矩阵操作：把数组作为矩阵来用

θ

julia> id  = reshape([1, 2, 3, 4], 2, 2)
2×2 Array{Int64,2}:
1  3
2  4


 julia> [1 2 3 4]


1x4 Array{Int64,2}:
1  2  3  4


julia> [[1, 2, 3], [4, 5, 6]]
2-element Array{Array{Int64,1},1}:
[1, 2, 3]
[4, 5, 6]


julia> [[1, 2, 3] [4, 5, 6]]
3×2 Array{Int64,2}:
1  4
2  5
3  6


访问数组的内容

julia> a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]


julia> a[5]
50


(因此，它属于 Matlab、Mathematica、Fortran、Lua 和 Smalltalk 等精英公司，而其他大多数编程语言则坚定地站在基于 0 的索引器的对立面。【译者注：应该是基于1的索引器的对立面？下为原文：】)

(And thus it's in the elite company of Matlab, Mathematica, Fortran, Lua, and Smalltalk, while most of the other programming languages are firmly in the opposite camp of 0-based indexers.)

julia> a[end]
100


julia> a[end-1]
90


(倒数第三个元素类似，依此类推)。

julia> a[[3,6,2]]
3-element Array{Int64,1}:
30
60
20


julia> a[2:2:end]
5-element Array{Int64,1}:
20
40
60
80
100


You can even select elements using true and false values:

julia> a[[true, true, false, true, true, true, false, true, false, false]]
6-element Array{Int64,1}:
10
20
40
50
60
80


julia> a2 = [1 2 3; 4 5 6; 7 8 9]
3x3 Array{Int64,2}:
1  2  3
4  5  6
7  8  9

julia> a2[1]
1


julia> a2[2]
4


julia> a2[1, 2]
2


julia> a2[1, 3]
3


julia> a2[1, 4]
ERROR: BoundsError: attempt to access 3×3 Array{Int64,2} at index [1, 4]
Stacktrace:
[1] getindex(::Array{Int64,2}, ::Int64, ::Int64) at ./array.jl:498


julia> getindex(a2, 1, 3)
3

julia> getindex(a2, 1, 4)
ERROR: BoundsError: attempt to access 3×3 Array{Int64,2} at index [1, 4]
Stacktrace:
[1] getindex(::Array{Int64,2}, ::Int64, ::Int64) at ./array.jl:498


julia> a2[:, 2]
3-element Array{Int64,1}:
2
5
8


julia> a2[2, :]
3-element Array{Int64,1}:
4
5
6


元素级操作和向量化操作

julia> a = collect(1:10);
julia> a * 2
10-element Array{Int64,1}:
2
4
6
8
10
12
14
16
18
20


julia> a / 100
10-element Array{Float64,1}:
0.01
0.02
0.03
0.04
0.05
0.06
0.07
0.08
0.09
0.1


julia> n1 = 1:6;
julia> n2 = 100:100:600;
julia> n1 .* n2
6-element Array{Int64,1}:
100
400
900
1600
2500
3600


julia> a = 1:10; b=rand(1:10, 10); a .== b
10-element BitArray{1}:
true
false
true
false
false
false
false
false
false
false


Broadcasting: dot syntax for vectorizing functions

julia> f(a, b) = a * b
f (generic function with 1 method)



julia> f(2, 3)
6


julia> f.([1, 4, 2, 8, 7], 10)
5-element Array{Int64,1}:
10
40
20
80
70

julia> f.(100, 1:10)
10-element Array{Int64,1}:
100
200
300
400
500
600
700
800
900
1000


min() 和 max()

julia> r = rand(0:10, 10)
10-element Array{Int64,1}:
3
8
4
3
2
5
7
3
10
10


julia> max(r)
LoadError: MethodError: no method matching max(::Array{Int64,1})
...


max 函数返回其参数中最大的一个。要查找数组中最大的元素，可以使用相关函数maximum()

julia> maximum(r)
10


julia> r = rand(0:10, 10); s = rand(0:10, 10); t = rand(0:10,10);

julia> max(r, s, t)
10-element Array{Int64,1}:
8
9
7
5
8
9
6
10
9
9


min()minimum() 的行为方式类似。

julia> max(r...)
9


julia> a = rand(0:10,10, 10)
10x10 Array{Int64,2}:
10   5   3   4  7   9  5   8  10   2
6  10   3   4  6   1  2   2   5  10
7   0   3   4  1  10  7   7   0   2
4   9   5   2  4   2  1   6   1   9
0   0   6   4  1   4  8  10   1   4
10   4   0   5  1   0  4   4   9   2
9   4  10   9  6   9  4   5   1   1
1   9  10  10  1   9  3   2   3  10
4   6   3   2  7   7  5   4   6   8
3   8   0   7  1   0  1   9   7   5


julia> a[a .== 0] .= 11;

julia> a
10x10 Array{Int64,2}:
10   5   3   4  7   9  5   8  10   2
6  10   3   4  6   1  2   2   5  10
7  11   3   4  1  10  7   7  11   2
4   9   5   2  4   2  1   6   1   9
11  11   6   4  1   4  8  10   1   4
10   4  11   5  1  11  4   4   9   2
9   4  10   9  6   9  4   5   1   1
1   9  10  10  1   9  3   2   3  10
4   6   3   2  7   7  5   4   6   8
3   8  11   7  1  11  1   9   7   5


行与列

julia> table = [r * c for r in 1:5, c in 1:5]
5x5 Array{Int64,2}:
1   2   3   4   5
2   4   6   8  10
3   6   9  12  15
4   8  12  16  20
5  10  15  20  25


julia> table[1, :]
1x5 Array{Int64,2}:
5-element Array{Int64,1}:
1
2
3
4
5


julia> table[2:3,:]
2x5 Array{Int64,2}:
2  4  6   8  10
3  6  9  12  15


julia> table[:, 2]
5-element Array{Int64,1}:
2
4
6
8
10


julia> table[:]
25-element Array{Int64,1}:
1
2
3
4
5
2
4
6
8
10
3
6
9
12
15
4
8
12
16
20
5
10
15
20
25


julia> table[:, 2:3]
5x2 Array{Int64,2}:
2   3
4   6
6   9
8  12
10  15


数组中查找元素

julia> a = 1:10

julia> 3 in a
true


julia> in(3, a) # needle ... haystack
true


julia> smallprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29];


julia> findfirst(isequal(13), smallprimes)
6


julia> smallprimes[6]
13


julia> findfirst(x -> x == 13, smallprimes)
6


findall() 函数返回索引数组，指向应用时函数返回true的每个元素：

julia> findall(isinteger, smallprimes)
10-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10

julia> findall(iseven, smallprimes)
1-element Array{Int64,1}:
1


julia> smallprimes[findall(isodd, smallprimes)]
9-element Array{Int64,1}:
3
5
7
11
13
17
19
23
29


julia> findfirst(iseven, smallprimes)
1

julia> smallprimes[findfirst(iseven, smallprimes)]
2


findnext() 函数与 findall()findfirst() 函数非常相似，但它接受一个额外的数字，告诉函数从数组中间的某个位置开始搜索，而不是从头开始。

julia> findnext(isodd, smallprimes, 1 + findfirst(isequal(13), smallprimes))
7

julia> smallprimes[ans]
17


findin(A, B) 函数返回数组 A 中的元素可在数组 B 中找到的元素的索引：

julia> findall(in([11, 5]), smallprimes)
2-element Array{Int64,1}:
3
5

julia> smallprimes[3]
5

julia> smallprimes[5]
11


Finding out about an array

julia> a2 = [1 2 3; 4 5 6; 7 8 9]
3x3 Array{Int64,2}:
1  2  3
4  5  6
7  8  9


• ndims()
• size()
• length()
• count()

ndims() 返回维数。向量为1，表为2，依此类推：

julia> ndims(a2)
2


size() 以元组的形式返回数组的行数和列数：

julia> size(a2)
(3,3)


length() 告诉您数组包含多少元素：

julia> length(a2)
9


julia> count(!iszero, a2)
9


julia> CartesianIndices(a2)[6]
CartesianIndex(3, 2)


julia> LinearIndices(a2)[3, 2]
6


数组比较

union() 构建一个新数组，该数组是两个或多个数组的联合或组合。该操作将删除重复项，并且结果包含每个元素的唯一版本：

julia> odds = collect(1:2:10)
5-element Array{Int64,1}:
1
3
5
7
9

julia> evens = collect(2:2:10)
5-element Array{Int64,1}:
2
4
6
8
10

julia> union(odds, evens)
10-element Array{Int64,1}:
1
3
5
7
9
2
4
6
8
10


julia> union(1:5, 1:10, 5:-1:-5)
16-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10
0
-1
-2
-3
-4
-5


intersect() 返回一个新数组，该数组是两个或多个数组的交集。结果包含每个元素的一个匹配项，但仅当它出现在每个数组中时：

julia> intersect(1:10, 5:15)
5:10

julia> intersect(5:20, 1:15, 3:12)
5:12


setdiff() 查找两个数组之间的不同，即 是第一个数组中的元素，而不是第二个数组中的元素：

julia> setdiff(1:15, 5:20)
4-element Array{Int64,1}:
1
2
3
4

julia> setdiff(5:20, 1:15)
5-element Array{Int64,1}:
16
17
18
19
20


过滤 filter

filter() 在元素通过测试时查找并保留这些元素。在这里，我们使用 isodd() 函数 (将其作为不带括号的命名函数传递，而不是带括号的函数调用) 来过滤（保留）所有 数组中是奇数的项。

julia> filter(isodd, 1:10)
5-element Array{Int64,1}:
1
3
5
7
9



julia> count(isodd, 1:100)
50


julia> any(isodd, 1:100)
true


all() 函数告诉您是否所有元素都满足条件。在这里 all() 检查 filter() 是否正确地完成了这项工作。

julia> all(isodd, filter(isodd, 1:100))
true


随机元素

julia> a = collect(1:100);
julia> a[rand(1:end)]
14



其他函数

julia> a = rand(100:110, 10)

10-element Array{Int64,1}:
109
102
104
108
103
110
100
108
101
101

julia> extrema(a)
(100,110)


findmax() 查找最大元素并在元组中返回它及其索引：

julia> findmax(a)
(110,6)


mean()middle() 已移到标准库的 Statistics 模块中，您可能需要首先输入 "using Statistics" 才能使用它们）

julia> sum(a)
1046

julia> prod(1:10)
3628800

julia> mean(a)
104.6

julia> middle(a)
105.0


sum(), mean(), and prod() 还允许提供函数：将该函数应用于每个元素，然后对结果求和/平均值/积：

julia> sum(sqrt, 1:10)  # the sum of the square roots of the first 10 integers 前10个整数的平方根之和
22.4682781862041

julia> mean(sqrt, 1:10) # the mean of the square roots of the first 10 integers 前十个证书的平方根的平均值
2.24682781862041


Combinatorics.jl 包中的函数允许您查找数组的组合和排列。 combinations() 查找数组中所有可能的元素组合：您可以指定每个组合中的元素数：

julia> Pkg.add("Combinatorics") # (do this just once)
julia> using Combinatorics
julia> collect(combinations(a, 3))
120-element Array{Array{Int64,1},1}:
[109,102,104]
[109,102,108]
[109,102,103]
[109,102,110]
[109,102,100]
[109,102,108]
[109,102,101]
[109,102,101]
[109,104,108]
[109,104,103]
[109,104,110]
[109,104,100]
[109,104,108]
⋮
[103,108,101]
[103,101,101]
[110,100,108]
[110,100,101]
[110,100,101]
[110,108,101]
[110,108,101]
[110,101,101]
[100,108,101]
[100,108,101]
[100,101,101]
[108,101,101]


julia> length(permutations(a))
3628800


改变数组内容: 添加删除元素

julia> a = collect(1:10); push!(a, 20)
11-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10
20


julia> pushfirst!(a, 0)
12-element Array{Int64,1}:
0
1
2
3
4
5
6
7
8
9
10
20


julia> a = [1, 2, 3, 5, 6, 7, 8, 9]
8-element Array{Int64,1}:
1
2
3
5
6
7
8
9


julia> splice!(a, 4:5, 4:6)
2-element Array{Int64,1}:
5
6


julia> a
9-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9


julia> L = ['a','b','f']
3-element Array{Char,1}:
'a'
'b'
'f'

julia> splice!(L, 3:2, ['c','d','e'])
0-element Array{Char,1}

julia> L
6-element Array{Char,1}:
'a'
'b'
'c'
'd'
'e'
'f'


删除元素

julia> a = collect(1:10);
julia> splice!(a,5);
julia> a
9-element Array{Int64,1}:
1
2
3
4
6
7
8
9
10


julia> pop!(a)
10


julia> popfirst!(a)
1


More aggressive modification of arrays (and similar data structures) can be made with functions such as deleteat!() and splice!(). You can find out the indices of elements in various ways. Once you know the indices, you can use deleteat!() to delete an element, given its index number:

julia> a = collect(1:10);

julia> findfirst(isequal(6), a)
4

julia> deleteat!(a, findfirst(isequal(6), a))
9-element Array{Int64,1}:
1
2
3
4
5
7
8
9
10


deleteat!() also accepts a range or iterator to specify the indices, so you can do this:

julia> deleteat!(a, 2:6)
4-element Array{Int64,1}:
1
8
9
10


Remember that you can always remove a group of elements using a filter: see Filtering.

其他函数

If you want to do something to an array, there's probably a function to do it, and sometimes with an exclamation mark to remind you of the potential consequences. Here are a few more of these array-modifying functions:

• resize!() change the length of a Vector
• append!() push a second collection at the back of the first one
• prepend!() insert elements at the beginning of the first Vector
• empty!(a) remove all elements
• rotr90(a) make a copy of an array rotated 90 degrees clockwise:
julia> rotr90([1 2 3 ; 4 5 6])
3x2 Array{Int64,2}:
4  1
5  2
6  3

• circshift(a) move the elements around 'in a circle' by a number of steps:
julia> circshift(1:6, 1)
6-element Array{Int64,1}:
6
1
2
3
4
5


This function can also do circular shifts on 2D arrays too. For example, here's a table:

julia> table = collect(r*c for r in 1:5, c in 1:5)
5×5 Array{Int64,2}:
1   2   3   4   5
2   4   6   8  10
3   6   9  12  15
4   8  12  16  20
5  10  15  20  25


By supplying a tuple you can move rows and columns. For example: moving the columns by 0 and the rows by 1 moves the first dimension by 0 and the second by 1. The first dimension is downwards, the second rightwards:

julia> circshift(table, (0, 1))
5×5 Array{Int64,2}:
5  1   2   3   4
10  2   4   6   8
15  3   6   9  12
20  4   8  12  16
25  5  10  15  20


There's a modifying version of circshift(), circshift!

设置数组内容

To set the contents of an array, specify the indices on the left-hand side of an assignment expression:

julia> a = collect(1:10);

julia> a[9]= -9
-9


To check that the array has really changed:

julia> print(a)
[1,2,3,4,5,6,7,8,-9,10]


You can set a bunch of elements at the same time, using the broadcasting assignment operator:

julia> a[3:6] .= -5
4-element view(::Array{Int64,1}, 3:6) with eltype Int64:
-5
-5
-5
-5
julia> print(a)
[1,2,-5,-5,-5,-5,7,8,-9,10]


And you can set a sequence of elements to a suitable sequence of values:

julia> a[3:9] = collect(9:-1:3)
7-element Array{Int64,1}:
9
8
7
6
5
4
3


Notice here that, although Julia shows the 7 element slice as the return value, in fact the whole array has been modified:

julia> a
10-element Array{Int64,1}:
1
2
9
8
7
6
5
4
3
10


You can set ranges to a single value in one operation using broadcasting:

julia> a[1:5] .= 0
0

julia> a
10-element Array{Int64,1}:
0
0
0
0
0
6
7
8
9
10

julia> a[1:10] .= -1;
-1

julia> print(a)
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]


As an alternative to the square bracket notation, there's a function call version that does the same job of setting array contents, setindex!():

julia> setindex!(a, 1:10, 10:-1:1)
10-element Array{Int64,1}:
10
9
8
7
6
5
4
3
2
1


You can refer to the entire contents of an array using the colon separator without start and end index numbers, i.e. [:]. For example, after creating the array a:

julia> a = collect(1:10);


we can refer to the contents of this array a using a[:]:

julia> b = a[:]
10-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10

julia> b[3:6]
4-element Array{Int64,1}:
3
4
5
6


把数组传递给函数

julia> function set_to_5(x)
x = 5
end
set_to_5 (generic function with 1 method)

julia> x = 3
3

julia> set_to_5(x)
5

julia> x
3


julia> function fill_with_5(x)
x[:] .= 5
end
fill_with_5 (generic function with 1 method)

julia> x = collect(1:10)
10-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10

julia> fill_with_5(x)
5

julia> x
10-element Array{Int64,1}:
5
5
5
5
5
5
5
5
5
5


If, instead of accessing the container variable's contents, you try to change the variable itself, it won't work. For example, the following function definition creates an array of 5s in temp and then attempts to change the argument x to be temp.

julia> function fail_to_fill_with_5(x)
temp = similar(x)
for i in 1:length(x)
temp[i] = 5
end
x = temp
end
fail_to_fill_with_5 (generic function with 1 method)

julia> x = collect(1:10)
10-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10

julia> fail_to_fill_with_5(x)
10-element Array{Int64,1}:
5
5
5
5
5
5
5
5
5
5


It looks like it worked, but:

julia> x
10-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10


You can change elements of the array, but you can't change the variable so that it points to a different array. In other words, your function isn't allowed to change the binding between the argument and the array that was passed to it.

Julia's way of handling function arguments is described as “pass-by-sharing”. An array isn't copied when you pass it to a function (that would be very inefficient for large arrays).

矩阵运算

For matrix-on-matrix arithmetic action, you can:

- add (+) and subtract (-):

θ

θ

θ

θ

- multiply (*), assuming the dimensions are compatible, so m1 * m2 is possible if last(size(m1)) == first(size(m2)). Note the difference between matrix multiplication and elementwise matrix multiplication. Here's a matrix A:

θ

and here's matrix B:

θ

The .* broadcasting operator multiplies them elementwise:

θ

Compare this with matrix multiplication, A * B:

θ

which is:

julia> [1 * 10 + 2 * 12        1 * 11 + 2 * 13  ;      3 * 10 + 4 * 12     3 * 11 + 4 * 13]
2x2 Array{Int64,2}:
34  37
78  85


- division of two matrices. You can use the backslash (\) for left division:

julia> A = rand(1:9, 3, 3)
3x3 Array{Int64,2}:
5  4  3
8  7  7
9  3  7

 julia> B = rand(1:9, 3, 3)
3x3 Array{Int64,2}:
6  5  5
6  7  5
7  2  7

 julia> A \ B
3x3 Array{Float64,2}:
2.01961    0.411765   1.84314
0.254902   1.35294   -0.0392157
-1.70588   -0.823529  -1.35294


and the forward slash (/) right or slash division:

julia> A / B
3x3 Array{Float64,2}:
4.0       -2.0       -1.0
0.285714   0.714286   0.285714
5.07143   -3.07143   -0.428571


With a matrix and a scalar, you can add, subtract, multiply, and divide:

julia> A + 1
3x3 Array{Int64,2}:
6  5  4
9  8  8
10  4  8

julia> [1 2 3 4 5] * 2
1x5 Array{Int64,2}:
2  4  6  8  10

julia> A .- 1
3x3 Array{Int64,2}:
4  3  2
7  6  6
8  2  6

julia> A .* 2
3x3 Array{Int64,2}:
10   8   6
16  14  14
18   6  14

julia> A ./ 2
3x3 Array{Float64,2}:
2.5  2.0  1.5
4.0  3.5  3.5
4.5  1.5  3.5


and more besides:

julia> A // 2
3x4 Array{Rational{Int64},2}:
1//2  2//1  7//2   5//1
1//1  5//2  4//1  11//2
3//2  3//1  9//2   6//1

julia> A .< 6
3x3 BitArray{2}:
true   true   true
false  false  false
false   true  false


You can multiply matrix and a vector (the matrix-vector product), if the arrays have compatible shapes. Here's the matrix A:

θ

and here's a vector V:

θ

The * operator multiplies them:

θ

The dot or inner product (aTb) can be found using the dot() function, but you'll have to import the LinearAlgebra library first:

julia> using LinearAlgebra

θ

julia> (1 * 21) + (2 * 22) +  (3 * 23)
134


The two arguments must have the same length. You can also use the dot operator, which you can obtain in the REPL by typing "\cdot" followed by a tab:

julia> [1:3] ⋅ [21:23]
134


连接数组和矩阵

You can use hcat() and vcat() to join matrices together, if their dimensions permit.

hcat() keeps the first dimension and extends (joins) in the second, vcat() keeps the second dimension and extends the first.

Here are two 3 by 4 matrices:

julia> A = reshape(1:12, 3, 4)
3x4 Array{Int64,2}:
1  4  7  10
2  5  8  11
3  6  9  12

julia> B = reshape(100:100:1200, 3, 4)
3x4 Array{Int64,2}:
100  400  700  1000
200  500  800  1100
300  600  900  1200


hcat(A, B) makes a new array that still has 3 rows, but extends/joins the columns to make 8 in total:

julia> hcat(A, B)
3x8 Array{Int64,2}:
1  4  7  10  100  400  700  1000
2  5  8  11  200  500  800  1100
3  6  9  12  300  600  900  1200


vcat(A, B) makes a new array that keeps the 4 columns, but extends to 6 rows:

julia> vcat(A, B)
6x4 Array{Int64,2}:
1    4    7    10
2    5    8    11
3    6    9    12
100  400  700  1000
200  500  800  1100
300  600  900  1200


You'll probably find the shortcuts useful:

• [A ; B ] is vcat(A, B)
• [A B ] is hcat(A, B)

vec() flattens a matrix into a vector, turning it into a (what some call a 'column') vector:

 julia> vec(ones(3, 4))
12-element Array{Float64,1}:
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0


There's also an hvcat() function ([A B; C D;]) that does both.

You can use hcat() to convert an array of arrays to a matrix (using the hcat-splat):

julia> a = Array[[1, 2], [3, 4], [5, 6]]
3-element Array{Array{T,N},1}:
[1, 2]
[3, 4]
[5, 6]

julia> hcat(a...)
2x3 Array{Int64,2}:
1  3  5
2  4  6



Julia arrays are 'column-major'. This means that you read down the columns:

 1  3
2  4


whereas 'row-major' arrays are to be read across, like this:

 1  2
3  4


Column-major order is used in Fortran, R, Matlab, GNU Octave, and by the BLAS and LAPACK engines (the "bread and butter of high-performance numerical computation"). Row-major order is used in C/C++, Mathematica, Pascal, Python, C#/CLI/.Net and others.

增广和扩展数组

Often you want to create an array and then add more to it, or 'grow' it. While can do this with vcat() and hcat(), be aware that both these operations create new temporary arrays and copy elements, so they don't always produce the fastest code. A better way is to use push!. This is an efficient operation that extends the array. You can reshape the array later:

julia> a = []
julia> for i = 1:80
push!(a, i)
end

julia> a
80-element Array{Any,1}:
1
2
3
4
5
6
7
8
9
⋮
75
76
77
78
79
80


reshape() lets you change the dimensions of an array. You can supply the dimensions or use a colon (:) to ask Julia to calculate valid dimensions:

julia> reshape(a, 10, :)
10x8 Array{Any,2}:
1  11  21  31  41  51  61  71
2  12  22  32  42  52  62  72
3  13  23  33  43  53  63  73
4  14  24  34  44  54  64  74
5  15  25  35  45  55  65  75
6  16  26  36  46  56  66  76
7  17  27  37  47  57  67  77
8  18  28  38  48  58  68  78
9  19  29  39  49  59  69  79
10  20  30  40  50  60  70  80


reshape(a, (10, div(length(a), 10))) would have the same effect.

push!() doesn't let you push new rows to a 2D array or matrix. The best way to do the job is to work on a 1D array, as above, adding more elements at the end, and then use reshape() to convert it to two dimensions. If necessary, use transpose() to flip the matrix.

操作矩阵

To transpose an array or matrix, there's an equivalent ' operator for the transpose() function, to swap rows and columns:

julia> M = reshape(1:12, 3, 4)
3×4 Base.ReshapedArray{Int64,2,UnitRange{Int64},Tuple{}}:
1  4  7  10
2  5  8  11
3  6  9  12

julia> transpose(M)
4x3 Array{Int64,2}:
1   2   3
4   5   6
7   8   9
10  11  12

julia> M'
4x3 Array{Int64,2}:
1   2   3
4   5   6
7   8   9
10  11  12


To find the determinant of a square matrix, use det(), after remembering to load the LinearAlgebra library.

julia> using LinearAlgebra
julia> A = rand(2:10, 3, 3)
3x3 Array{Int64,2}:
8  8   2
6  9   6
9  2  10

julia> det(A)
438.00000000000006


inv() (in the Standard Library) finds the inverse of a square matrix, if it has one. (If the determinant of the matrix is zero, it won't have an inverse.)

julia> inv(A)
3x3 Array{Float64,2}:
0.178082   -0.173516   0.0684932
-0.0136986   0.141553  -0.0821918
-0.157534    0.127854   0.0547945


LinearAlgebra.rank() finds the rank of the matrix, and LinearAlgebra.nullspace() finds the basis for the nullspace.

julia> A
3x4 Array{Int64,2}:
1  4  7  10
2  5  8  11
3  6  9  12

julia> rank(A)
2

julia> nullspace(A)
4x2 Array{Float64,2}:
-0.475185  -0.272395
0.430549   0.717376
0.564458  -0.617566
-0.519821   0.172585


LinearAlgebra.tr() sums the diagonal of a square matrix (trace):

julia> s = reshape(1:9, 3, 3)
3x3 Array{Int64,2}:
1  4  7
2  5  8
3  6  9

julia> tr(s)
15


对矩阵调用函数

There are a number of functions that can be applied to a matrix:

- sum() adds every element:

julia> A = reshape(1:9, 3, 3)
3×3 Base.ReshapedArray{Int64,2,UnitRange{Int64},Tuple{}}:
1  4  7
2  5  8
3  6  9

julia> sum(A)
45


You can specify a dimension if you want to sum just columns or rows. So to sum columns, specify dimension 1:

julia> sum(A, dims=(1))
1x3 Array{Int64,2}:
6  15  24


To sum rows, specify dimension 2:

julia> sum(A, dims=(2))
3x1 Array{Int64,2}:
12
15
18


- mean() finds the mean of the values in the matrix:

julia> using Statistics; mean(A)
5.0


As with sum(), you can specify a dimension, so that you can find the mean of columns (use dimension 1) or rows (use dimension 2):

julia> mean(A, dims=(1))
1x3 Array{Float64,2}:
2.0  5.0  8.0

julia> mean(A, dims=(2))
3x1 Array{Float64,2}:
4.0
5.0
6.0


- the min(A, B) and max(A, B) functions compare two (or more) arrays element by element, returning a new array with the largest (or smallest) values from each:

julia> A = rand(-1:2:1, 3, 3)
3x3 Array{Int64,2}:
-1  -1  -1
-1   1   1
1  -1   1

julia> B = rand(-2:4:2, 3, 3)
3x3 Array{Int64,2}:
2   2  2
2  -2  2
2   2  2


prod() multiplies a matrix's elements together:

julia> A = reshape(collect(BigInt(1):25), 5, 5)
5×5 Array{BigInt,2}:
1   6  11  16  21
2   7  12  17  22
3   8  13  18  23
4   9  14  19  24
5  10  15  20  25

julia> prod(A)
15511210043330985984000000


(Notice the use of BigInt, products are very large.)

You can specify a dimension if you want to multiply just columns or rows. To multiply the elements of columns together, specify dimension 1; for rows, use dimension 2:

julia> prod(A, 1)
1x5 Array{Int64,2}:
120  30240  360360  1860480  6375600

julia> prod(A, 2)
5x1 Array{Int64,2}:
22176
62832
129168
229824
375000


矩阵范数

julia> using LinearAlgebra



向量范数

julia> X = [2, 4, -5]
3-element Array{Int64,1}:
2
4
-5

julia> LinearAlgebra.norm(X) # Euclidean norm
6.708203932499369

julia> LinearAlgebra.norm(x, 1) # 1-norm of the vector, the sum of element magnitudes
11.0


julia> X = [2 4 -5]
1x3 Array{Int64,2}:
2  4  -5

julia> LinearAlgebra.norm(X)
6.708203932499369

julia> LinearAlgebra.norm(X, 1)
5.0


julia> LinearAlgebra.norm([1 2 3] - [2 4 6])
3.741657386773941

julia> LinearAlgebra.norm([1, 2, 3] - [2, 4, 6])
3.741657386773941


The angle between two vectors ${\displaystyle a}$ and ${\displaystyle b}$ is ${\displaystyle \arccos({\frac {a^{T}b}{\|a\|_{2}\|b\|_{2}}})}$:

acos(dot(a,b)/(norm(a)*norm(b)))


矩阵范数

Here's the 1-norm of a matrix (the maximum absolute column sum):

julia> B = [5 -4 2 ; -1 2 3; -2 1 0]
3x3 Array{Int64,2}:
5  -4  2
-1   2  3
-2   1  0

julia> LinearAlgebra.norm(B, 1)
8.0


And here's the infinity norm (the maximum absolute row sum):

julia> LinearAlgebra.norm(B, Inf)
11.0


The Euclidean norm() is the default:

julia> LinearAlgebra.norm([2 3 ; 4 6]), sqrt(2^2 + 3^2 + 4^2 + 6^2)
(8.062257748298547,8.06225774829855)


Scaling and rotating matrices

- rmul!(A, n) scales every element of the matrix in place by a scale factor n:

julia> A = [1 2 3
4 5 6
7 8 9]
3×3 Array{Int64,2}:
1  2  3
4  5  6
7  8  9

julia> rmul!(A, 2)
3×3 Array{Int64,2}:
2   4   6
8  10  12
14  16  18


There are rotation and circular-shifting functions too:

julia> A = [1 2 3
4 5 6
7 8 9]
3×3 Array{Int64,2}:
1  2  3
4  5  6
7  8  9

julia> rot180(A)
3×3 Array{Int64,2}:
9  8  7
6  5  4
3  2  1

julia> circshift(A, (1, 1))
3×3 Array{Int64,2}:
9  7  8
3  1  2
6  4  5

julia> A
3×3 Array{Int64,2}:
1  2  3
4  5  6
7  8  9



reverse() makes a copy of a matrix reversing rows or columns:

julia> reverse(A, dims=(1))
3×3 Array{Int64,2}:
7  8  9
4  5  6
1  2  3

julia> reverse(A, dims=(2))
3×3 Array{Int64,2}:
3  2  1
6  5  4
9  8  7


squeeze() and reshape() can be used to change the dimensions of a matrix. For example, this is how you can use squeeze() to collapse a row vector (1 by 4) into a 4 by 1 array:

julia> a = [1 2 3 4]
1x4 Array{Int64,2}:
1  2  3  4

julia> ndims(a)
2

julia> b = squeeze(a, dims=(1))
4-element Array{Int64,1}:
1
2
3
4

julia> ndims(b)
1


数组排序

Julia has a flexible sort() function that returns a sorted copy of an array, and a companion sort!() version that changes the array so that it's sorted.

You can usually use sort() without options and obtain the results you'd hoped for:

julia> using Random
julia> rp = randperm(10)
10-element Array{Int64,1}:
6
4
7
3
10
5
8
1
9
2

julia> sort(rp)
10-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10


You can sort 2D arrays:

julia> a = reshape(rand(1:20, 20), 4, 5)
4x5 Array{Int64,2}:
19  13   4  10  10
6  20  19  18  12
17   7  15  14   9
1  16   8   7  13

julia> sort(a, dims=(1)) # sort each column, dimension 1
4x5 Array{Int64,2}:
1   7   4   7   9
6  13   8  10  10
17  16  15  14  12
19  20  19  18  13

julia> sort(a, dims=(2)) # sort each row, dimension 2
4x5 Array{Int64,2}:
4  10  10  13  19
6  12  18  19  20
7   9  14  15  17
1   7   8  13  16


although there are more powerful alternatives in sortrows() and sortcolumns() — see below for details.

The sortperm() function is similar to sort(), but it doesn't return a sorted copy of the collection. Instead it returns a list of indices that could be applied to the collection to produce a sorted version:

julia> r = rand(100:110, 10)
10-element Array{Int64,1}:
103
102
110
108
108
108
104
109
106
106

julia> sortperm(r)
10-element Array{Int64,1}:
2
1
7
9
10
4
5
6
8
3

julia> r[sortperm(r)]
10-element Array{Int64,1}:
102
103
104
106
106
108
108
108
109
110


排序和比较

If you need more than the default sort() offers, use the by and lt keywords and provide your own functions for processing and comparing elements during the sort.

sort by

The by function processes each element before comparison and provides the 'key' for the sort. A typical example is the task of sorting a list of numbers in string form into numerical order. Here's the list:

julia> r = ["1E10", "150", "25", "3", "1.5", "1E-10", "0.5", ".999"];


If you use the default sort, the numbers appear in the order in which the characters appear in Unicode/ASCII:

julia> sort(r)
8-element Array{ASCIIString,1}:
".999"
"0.5"
"1.5"
"150"
"1E-10"
"1E10"
"25"
"3"


with "1E-10" appearing after "0.999".

To sort the numbers by their value, pass the parse() function (from the Meta package) to by:

julia> sort(r, by = x -> Meta.parse(x))
8-element Array{String,1}:
"1E-10"
"0.5"
".999"
"1.5"
"3"
"25"
"150"
"1E10"


The strings are sorted 'by' their value. Notice that the by function you supply produces the numerical sort key, but the original string elements appear in the final result.

Anonymous functions can be useful when sorting arrays. Here's a 10 rows by 2 columns array of tuples:

julia> table = collect(enumerate(rand(1:100, 10)))
10-element Array{(Int64,Int64),1}:
(1,86)
(2,25)
(3,3)
(4,97)
(5,89)
(6,58)
(7,27)
(8,93)
(9,98)
(10,12)


You can sort this array by the second element of each tuple, not the first, by supplying an anonymous function to by that points to the second element of each. The anonymous function says, given an object x to sort, sort by the second element of x:

julia> sort(table, by = x -> x[2])
10-element Array{(Int64,Int64),1}:
(3,3)
(10,12)
(2,25)
(7,27)
(6,58)
(1,86)
(5,89)
(8,93)
(4,97)
(9,98)

Sorting by multiple columns

You can supply a tuple of "column" identifiers in the by function, if you want to sort by more than one column.

julia>  a = [[2, 2, 2, 1],
[1, 1, 1, 8],
[2, 1, 2, 2],
[1, 2, 2, 5],
[2, 1, 1, 4],
[1, 1, 2, 7],
[1, 2, 1, 6],
[2, 2, 1, 3]] ;

julia> sort(a, by = col -> (col[1], col[2], col[3]))
8-element Array{Array{Int64,1},1}:
[1,1,1,8]
[1,1,2,7]
[1,2,1,6]
[1,2,2,5]
[2,1,1,4]
[2,1,2,2]
[2,2,1,3]
[2,2,2,1]


This sorts the array first by column 1, then by column 2, then by column 3.

Redefining 'less than'

By default, sorting uses the built-in isless() function when comparing elements. In a sorted array, the first element is less than the second.

You can change this behaviour by passing a different function to the lt keyword. This function should compare two elements and return true if they're sorted, i.e. if the first element is 'less than' the second, using some definition of 'less than'. The sorting process compares pairs of elements repeatedly until every element of the array is in the right place.

For example, suppose you want to sort an array of words according to the number of vowels in each word; i.e. the more vowels a word has, the earlier in the sorted results it occurs. For example, the word "orange" will be considered to be "less than" the word "lemon", because it has more vowels.

First we'll need a function that counts vowels:

vowelcount(string) = count(c -> (c in "aeiou"), lowercase(string))


Now you can pass an anonymous function to sort() that compares the vowel count of two elements using this function and then returns the element with a higher count in each case:

 sentence = split("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
sort(sentence, lt = (x,y) -> vowelcount(x) > vowelcount(y))


The result is that the word with the most vowels appears first:

 19-element Array{SubString{String},1}:

 

"adipisicing" "consectetur" "eiusmod" "incididunt" "aliqua." "labore" "dolore" "Lorem" "ipsum" "dolor" "amet," "elit," "tempor" "magna" "sit" "sed" "do" "ut" "et" 

The sort() function also lets you specify a reverse sort - after the by and lt functions (if used) have done their work, a true value passed to rev reverses the result.

二维数组排序

In Julia 1.0, you can sort multidimensional arrays with sortslices().

Here's a simple array of nine strings (you can also use numbers, symbols, functions, or anything that can be compared):

julia> table = ["F" "B" "I"; "A" "D" "G"; "H" "C" "E"]
3×3 Array{String,2}:
"F"  "B"  "I"
"A"  "D"  "G"
"H"  "C"  "E"


You supply a number or a tuple to the dims ("dimensions") keyword that indicates what you want to sort. To sort the table so that the first column is sorted, use 1:

julia> sortslices(table, dims=1)
3×3 Array{String,2}:
"A"  "D"  "G"
"F"  "B"  "I"
"H"  "C"  "E"


Note that sortslices returns a new array. The first column is in alphabetical order.

Use dims=2 to sort the table so that the first row is sorted:

julia>> sortslices(table, dims=2)
3×3 Array{String,2}:
"B"  "F"  "I"
"D"  "A"  "G"
"C"  "H"  "E"


Now the first row is in alphabetical order.

If you want to sort by something other than the first item, pass a function to by. So, to sort rows so that the middle column is in alphabetical order, use:

julia> sortslices(table, dims=1, by = x -> x[2])
3×3 Array{String,2}:
"F"  "B"  "I"
"H"  "C"  "E"
"A"  "D"  "G"


sortslices has most of the options that you'll find in sort, and more besides. You can reverse the order with rev, change the comparator with  lt, and so on.

元组

A tuple is an ordered sequence of elements, like an array. A tuple is represented by parentheses and commas, rather than the square brackets used by arrays. Tuples are mostly good for small fixed-length collections — they're used everywhere in Julia, for example, as argument lists and for returning multiple values from functions.

The important difference between arrays and tuples is that tuples are immutable. Other than that, tuples work in much the same way as arrays, and many array functions can be used on tuples too:

julia> t = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
(1,2,3,4,5,6,7,8,9,10)

julia> t
(1,2,3,4,5,6,7,8,9,10)

julia> t[6:end]
(6,7,8,9,10)


You can have two-dimensional tuples:

julia> t = ((1, 2), (3, 4))
((1,2),(3,4))

julia> t[1]
(1,2)

julia> t[1][2]
2


But you can't change a tuple:

julia> t[1] = 0
LoadError: MethodError: no method matching set index!...


And, because you can't modify tuples, you can't use any of the functions like push!() that you use with arrays:

julia> a = [1,2,3];
julia> push!(a,4)
4-element Array{Int64,1}:
1
2
3
4

julia> t = (1,2,3);
julia> push!(t,4)
LoadError: MethodError: no method matching push!


命名的元组

A named tuple is like a combination of a tuple and a dictionary. Like a tuple, a named tuple is ordered and immutable, and enclosed in parentheses; like a dictionary, each element has a unique key that can be used to access it.

You can create a named tuple by providing keys and values directly:

julia> shape1 = (corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0))

(corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0))


To access the values, use the familiar dot syntax:

julia> shape1.corner1
(1, 1)

julia> shape1.center
(0, 0)

julia> (shape1.corner1, shape1.corner2)
((1, 1), (-1, -1))


You can access all the values (destructuring) as with ordinary tuples:

julia> c1, c2, centerp = shape1;

julia> c1
(1, 1)

julia> c2
(-1, -1)


or just some of them:

julia> c1, c2 = shape1;

julia> c1
(1, 1)

julia> c2
(-1, -1)


Elements can be the same type, or different types, but the keys will always be variable names.

You can iterate over a named tuple:

julia> for i in shape1
@show i
end

i = (1, 1)
i = (-1, -1)
i = (0, 0)

julia> for i in shape1
println(first(i))
end

1
-1
0


Another way to create a named tuple is to provide the keys and values in separate tuples:

julia> ks = (:corner1, :corner2)
(:corner1, :corner2)

julia> vs = ((10, 10), (20, 20))
((10, 10), (20, 20))

julia> shape2 = NamedTuple{ks}(vs)
(corner1 = (10, 10), corner2 = (20, 20))

julia>shape2.corner1
(10, 10)

julia> shape2.corner2
(20, 20)


You can combine two named tuples to make a new one:

julia> colors = (top = "red", bottom = "green")
(top = "red", bottom = "green")

julia> merge(shape2, colors)
(corner1 = (10, 10), corner2 = (20, 20), top = "red", bottom = "green")


You can use existing variables for keys:

julia> d = :density;

julia> (corner1 = (10, 10), corner2 = (20, 20), d => 0.99)

(corner1 = (10, 10), corner2 = (20, 20), density = 0.99)


Making single value Named Tuples requires a strategically-placed comma:

julia> shape3 = (corner1 = (1, 1),)

(corner1 = (1, 1),)

julia> typeof(shape3)
NamedTuple{(:corner1,),Tuple{Tuple{Int64,Int64}}}


If you forget it, you'll see this:

julia> (corner1 = (1, 1))
(1, 1)

julia> typeof(corner1)
Tuple{Int64,Int64}


You can make new named tuples by combining named tuples together.

julia> shape3 = merge(shape2, colors)
(corner1 = (10, 10), corner2 = (20, 20), top = "red", bottom = "green")



Use a comma after a single element named tuple:

julia> merge(shape2, (top = "green",))
(corner1 = (10, 10), corner2 = (20, 20), top = "green")


To iterate over the "keys", use the fieldnames() and typeof() functions:

julia> fieldnames(typeof(shape3))
(:corner1, :corner2, :top, :bottom)


so you can do:

julia> for key in fieldnames(typeof(shape3))
@show getindex(shape3, key)
end

getindex(shape3, key) = (10, 10)
getindex(shape3, key) = (20, 20)
getindex(shape3, key) = "red"
getindex(shape3, key) = "green"


julia> shape3
(corner1 = (10, 10), corner2 = (20, 20), top = "red", bottom = "green")


julia> merge(shape3, (center = (0, 0), top="green"))

(corner1 = (10, 10), corner2 = (20, 20), top = "green", bottom = "green", center = (0, 0))


 « Introducing JuliaArrays and tuples » The REPL Types

Types

 « Introducing JuliaTypes » Arrays and tuples Controlling the flow

Types

Types of type

0.5  1//2  1


Julia 的设计师的官方说法是，类型是可选的。换句话说，如果您不想担心类型(如果您不介意代码的运行速度慢于它可能)，那么您可以忽略它们。但是您将在错误消息和文档中遇到它们，因此您最终将不得不处理它们…

类型系统

类型层次

julia> supertype(Number)
Any


julia> subtypes(Number)
2-element Array{Union{DataType, UnionAll},1}:
Complex
Real


julia> abstract type Feline end
julia> mutable struct Jaguar <: Feline end
julia> mutable struct Lion <: Feline end
julia> subtypes(Feline)
2-element Array{Any,1}:
Jaguar
Lion


具体和抽象类型

Julia中的每个对象(非正式地说，这意味着在Julia中可以放入变量的所有内容)都有一个类型。但并非所有类型都可以有各自的对象(该类型的实例)。唯一可以有实例的是所谓的具体类型。这些类型不能有任何子类型。可以有子类型(例如Any, Number)的类型称为抽象类型。因此，我们不能拥有Number类型的对象，因为它是抽象类型。换句话说，只有类型树的叶子是具体的类型，并且可以实例化。

 #this function gets a number, and returns the same number plus one
function plus_one(n::Number)
return n + 1
end


• 基本整数和浮点数类型(有符号和无符号)： Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128, Float16, Float32, and Float64
• 更高级的数字类型：BigFloat, BigInt
• 布尔和角色类型： Bool and Char
• 文本字符串类型： String

调查一下类型

Julia 提供了两个用于导航类型层次结构的函数：subtypes()supertype()

julia> subtypes(Integer)
4-element Array{Union{DataType, UnionAll},1}:
BigInt
Bool
Signed
Unsigned

julia> supertype(Float64)
AbstractFloat


sizeof() 函数告诉您此类型的项占用的字节数：

julia> sizeof(BigFloat)
32

julia> sizeof(Char)
4


julia> typemax(Int64)
9223372036854775807

julia> typemin(Int32)
-2147483648


 function showtypetree(T, level=0)
println("\t" ^ level, T)
for t in subtypes(T)
if t != Any
showtypetree(t, level+1)
end
end
end

showtypetree(Number)


julia> showtypetree(Number)
Number
Complex
Real
AbstractFloat
BigFloat
Float16
Float32
Float64
Integer
BigInt
Bool
Signed
Int128
Int16
Int32
Int64
Int8
Unsigned
UInt128
UInt16
UInt32
UInt64
UInt8
Irrational
Rational


指定变量类型

julia> collect(1:10)
10-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10

julia> collect(1.0:10)
10-element Array{Float64,1}:
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
9.0
10.0


julia> fill!(Array{String}(undef, 3), "Julia")
3-element Array{String,1}:
"Julia"
"Julia"
"Julia"


function f(x::Int64)

类型稳定性

function t1(n)
s = 0
t = 1
for i in 1:n
s += s/i
t = div(t, i)
end
return t
end

function t2(n)
s  = 0.0
t = 1
for i in 1:n
s += s/i
t = div(t, i)
end
return t
end


julia> @time t1(10000000)
0.658128 seconds (60.00 M allocations: 915.520 MiB, 25.01% gc time)

julia> @time t2(10000000)
0.118332 seconds (4 allocations: 160 bytes)


t1() 的表现明显低于 t2(). 原因是 s 一开始是一个整数。(s = 0 表示 Julia 最初将 s 视为整数), 但是在循环中，它被分配用来保存s/i 的结果，这是一个浮点值：必须将其从整数转换为浮点以进行匹配。因此此该函数不是类型稳定的，Julia编译器无法对其内容进行假设，也就不能生成纯整数代码或纯浮点代码。因此，它最终生成的代码并不像它写的时候所能产生的那样快。

创建类型

In Julia, it's very easy for the programmer to create new types, benefiting from the same performance and language-wise integration that the native types (those made by Julia's creators) have.

抽象类型

abstract type MyAbstractType end


julia> supertype(MyAbstractType)
Any


abstract type MyAbstractType2 <: Number end


julia> supertype(MyAbstractType2)
Number


具体类型和组合

 mutable struct MyType <: MyAbstractType
foo
bar::Int
end


julia> x = MyType("Hello World!", 10)
MyType("Hello World!", 10)


julia> x.foo
"Hello World!"

julia> x.bar
10


julia> x.foo = 3.0
3.0

julia> x.foo
3.0


julia> x.bar = "Hello World!"
LoadError: MethodError: Cannot convert an object of type String to an object of type Int64
This may have arisen from a call to the constructor Int64(...),
since type constructors fall back to convert methods.


例子：英国货币

 struct LSD


   pounds::Int
shillings::Int
pence::Int


  function LSD(a,b,c)
if a < 0 || b < 0 || c < 0
error("no negative numbers")
end
if c > 12 || b > 20
error("too many pence or shillings")
end
new(a, b, c)
end


end


struct LSD
pounds::Int
shillings::Int
pence::Int

function LSD(a, b, c)
if a < 0 || b < 0
error("no negative numbers")
end
if c > 12 || b > 20
error("too many pence or shillings")
end
new(a, b, c)
end
end


julia> price1 = LSD(5, 10, 6)
LSD(5, 10, 6)

julia> price2 = LSD(1, 6, 8)
LSD(1, 6, 8)


julia> price = LSD(1,0,13)
ERROR: too many pence or shillings
Stacktrace:
[1] LSD(::Int64, ::Int64, ::Int64)


julia> fieldnames(typeof(price1))
3-element Array{Symbol,1}:
:pounds
:shillings
:pence



julia> price1.pounds
5
julia> price1.shillings
10
julia> price1.pence
6


julia> price1 + price2
ERROR: MethodError: no method matching +(::LSD, ::LSD)
Closest candidates are:
+(::Any, ::Any, ::Any, ::Any...) at operators.jl:420


julia> price2
LSD(5, 10, 6)


Julia已经有了加法函数(+)，其中包含为许多类型的对象定义的方法。下面的代码添加了另一个可以处理两个LSD对象的方法：

function Base.:+(a::LSD, b::LSD)
newpence = a.pence + b.pence
newshillings = a.shillings + b.shillings
newpounds = a.pounds + b.pounds
subtotal = newpence + newshillings * 12 + newpounds * 240
(pounds, balance) = divrem(subtotal, 240)
(shillings, pence) = divrem(balance, 12)
LSD(pounds, shillings, pence)
end


julia> price1 + price2
LSD(6,17,2)


function Base.show(io::IO, money::LSD)
print(io, "£$(money.pounds).$(money.shillings)s.$(money.pence)d") end  在这里，io 是所有 show() 方法当前使用的输出通道。我们添加了一个简单的表达式，用适当的标点符号和分隔符显示字段值。 julia> println(price1 + price2) £6.17s.2d  julia> show(price1 + price2 + LSD(0,19,11) + LSD(19,19,6)) £27.16s.7d  可以添加一个或多个别名，这些别名是特定类型的备用名称。由于 Price 是 LSD 的一种更好的表达方式，我们将创建一个有效的替代方案： julia> const Price=LSD LSD julia> show(Price(1, 19, 11)) £1.19s.11d  到目前为止，还不错，但这些 LSD 对象还没有完全开发出来。如果要执行减法、乘法和除法运算，则必须为这些函数定义用于处理LSD的其他方法。减法很简单，只要用先令和便士来摆弄就行了，所以我们暂时不谈这个问题，但是乘法呢？价格乘以数字涉及两种类型的对象，一种是 Price / LSD对象，另一种是--嗯，任何正实数都应该是可能的： function Base.:*(a::LSD, b::Real) if b < 0 error("Cannot multiply by a negative number") end totalpence = b * (a.pence + a.shillings * 12 + a.pounds * 240) (pounds, balance) = divrem(totalpence, 240) (shillings, pence) = divrem(balance, 12) LSD(pounds, shillings, pence) end  与我们添加到 Base 的 + 函数中的 + 方法一样，Base 的 * 函数的这种新的 * 方法专门定义为将价格乘以数字。第一次尝试的效果出乎意料的好： julia> price1 * 2 £11.1s.0d julia> price1 * 3 £16.11s.6d julia> price1 * 10 £55.5s.0d julia> price1 * 1.5 £8.5s.9d julia> price3 = Price(0,6,5) £0.6s.5d julia> price3 * 1//7 £0.0s.11d  然而，有些失败是可以预料到的。我们没有考虑到一分钱中真正过时的部分：半分钱和一分钱： julia> price1 * 0.25 ERROR: InexactError() Stacktrace: [1] convert(::Type{Int64}, ::Float64) at ./float.jl:675 [2] LSD(::Float64, ::Float64, ::Float64) at ./REPL[36]:40 [3] *(::LSD, ::Float64) at ./REPL[55]:10  (答案应该是 1.7欧元7又二分之一欧元。不幸的是，我们的 LSD 类型不允许一分钱的零碎。) 但还有一个更紧迫的问题。此时此刻，你必须先给出价格，然后再乘以乘数；反过来说，结果是失败的： julia> 2 * price1 ERROR: MethodError: no method matching *(::Int64, ::LSD) Closest candidates are: *(::Any, ::Any, ::Any, ::Any...) at operators.jl:420 *(::Number, ::Bool) at bool.jl:106 ...  这是因为，尽管Julia可以找到匹配的方法 (a::LSD, b::Number) ，但却找不到另一种方法：(a::Number, b::LSD)。但是添加它是非常容易的： function Base.:*(a::Number, b::LSD) b * a end  它向Base的 * 函数添加了另一个方法。 julia> price1 * 2 £11.1s.0d  julia> 2 * price1 £11.1s.0d  julia> for i in 1:10 println(price1 * i) end £5.10s.6d £11.1s.0d £16.11s.6d £22.2s.0d £27.12s.6d £33.3s.0d £38.13s.6d £44.4s.0d £49.14s.6d £55.5s.0d  现在的价格看起来就像一家19世纪的英国老店。真的！ 如果您想查看到目前为止已添加了多少个方法来处理这种旧的英磅类型，请使用 methodswith 函数： julia> methodswith(LSD)  4-element Array{Method,1}: *(a::LSD, b::Real) at In[20]:4 *(a::Number, b::LSD) at In[34]:2 +(a::LSD, b::LSD) at In[13]:2 show(io::IO, money::LSD) at In[15]:2  到目前为止只有四个……您还可以继续添加方法，使该类型更加有用。这将取决于您对自己或其他人使用它的设想。例如，您可能希望添加除法和模数方法，并对负货币值进行智能操作。 可变结构 这种用于持有英国价格的复合类型被定义为不可变类型。创建价格对象后，不能更改这些对象的值： julia> price1.pence 6 julia> price1.pence=10 ERROR: type LSD is immutable  要基于现有价格创建新价格，您必须执行以下操作： julia> price2 = Price(price1.pounds, price1.shillings, 10) £5.10s.10d  对于这个特定的示例，这不是一个大问题，但是当您可能希望修改或更新某个类型中的字段的值，而不是创建一个具有正确值的新字段时，会出现许多应用场景。 对于这些情况，您需要创建一个可变结构 mutable struct。根据对类型的要求，在 structmutable struct 之间进行选择。 有关模块和从其他模块导入函数的更多信息，请参见 Modules and packages  « Introducing JuliaTypes » Arrays and tuples Controlling the flow Controlling the Flow  « Introducing JuliaControlling the flow » Types Functions 控制流程的不同方法 通常，Julia程序的每一行都是依次计算的。有多种方法可以控制和修改评估流程。它们与其他语言中使用的结构相对应： • 三元 复合表达式 • 布尔转换表达式 • if elseif else end - 条件求值 • for end - 迭代求值 • while end - 迭代条件求值 • try catch error throw 异常处理 • do 代码块 三元表达式 通常，如果某个条件为真，您会希望执行job A (或调用函数A)，如果不是，则希望执行 job B(函数B)。写流程控制最快的方法是使用三元运算符("?" 和 ":")： julia> x = 1 1 julia> x > 3 ? "yes" : "no" "no" julia> x = 5 5 julia> x > 3 ? "yes" : "no" "yes"  下面是另一个例子： julia> x = 0.3 0.3 julia> x < 0.5 ? sin(x) : cos(x) 0.29552020666133955  Julia 返回 sin(x) 的值， 因为 x 小于 0.5. cos(x) 根本没有被计算求值。 布尔转换表达式 布尔运算符使您可以在条件为真时对表达式求值。可以使用 &&|| 组合条件和表达式。&& 表示“和”，而 || 表示“或”。由于 Julia 逐个计算表达式，因此仅当前一个条件为 true 或 false 时，才能轻松安排对表达式求值。 下面的示例使用一个 Julia 函数，该函数根据数字是否为奇数返回 true或 false ：isodd(n) 对于 &&，这两个部分都必须是 true ，所以我们可以这样写： julia> isodd(1000003) && @warn("That's odd!") WARNING: That's odd! julia> isodd(1000004) && @warn("That's odd!") false  如果第一个条件(数字为奇数)为真，则计算第二个表达式。如果第一个不为真，则不对表达式求值，只返回条件。 另一方面，使用 || 运算符： julia> isodd(1000003) || @warn("That's odd!") true julia> isodd(1000004) || @warn("That's odd!") WARNING: That's odd!  如果第一个条件为 true，则不需要对第二个表达式求值，因为我们已经有“或”所需的一个真值，并且它返回值true。如果第一个条件为false，则对第二个表达式求值，因为该表达式可能被证明为真。 这类求值也称为“短路求值”。 If and Else 对于更一般和传统的条件执行方法，您可以使用 if, elseifelse。如果您习惯使用其他语言，请不要担心空格、大括号、缩进、括号、分号或诸如此类的东西，但请记住使用end完成条件的构造。 name = "Julia" if name == "Julia" println("I like Julia") elseif name == "Python" println("I like Python.") println("But I prefer Julia.") else println("I don't know what I like") end  elseifelse 的这部分是可选的： name = "Julia" if name == "Julia" println("I like Julia") end  别忘了 end! "switch"和"case"语句呢？emmmm，你不需要学习这些语法，因为他们不存在！ ifelse 有一个 ifelse 函数。看起来是这样的： julia> s = ifelse(false, "hello", "goodbye") * " world"  ifelse 是一个普通函数，它计算所有参数，并根据第一个参数的值返回第二个或第三个参数。使用条件 if? ... :，只对所选路由中的表达式求值。或者，也可以这样: julia> x = 10 10  julia> if x > 0 "positive" else "negative or zero" end "positive"  julia> r = if x > 0 "positive" else "negative or zero" end "positive" julia> r "positive"  for循环 和 迭代 遍历一个列表或一组值或从起始值到终止值都是迭代的样例，而 for ... end 构造可以让您遍历许多不同类型的对象，包括范围、数组、集、字典和字符串。 下面是通过一系列值进行简单迭代的标准语法： julia> for i in 0:10:100 println(i) end 0 10 20 30 40 50 60 70 80 90 100  变量 i 依次获取数组中每个元素的值(它是从 Range 对象构建的)：这里以10为步长从0到100。 julia> for color in ["red", "green", "blue"] # an array print(color, " ") end red green blue  julia> for letter in "julia" # a string print(letter, " ") end j u l i a  julia> for element in (1, 2, 4, 8, 16, 32) # a tuple print(element, " ") end 1 2 4 8 16 32  julia> for i in Dict("A"=>1, "B"=>2) # a dictionary println(i) end "B"=>2 "A"=>1  julia> for i in Set(["a", "e", "a", "e", "i", "o", "i", "o", "u"]) println(i) end e o u a i  我们还没有见过集和字典，但是遍历它们是完全一样的。 可以遍历二维数组，从上到下逐步“向下”遍历第1列，然后遍历第2列，依此类推： julia> a = reshape(1:100, (10, 10)) 10x10 Array{Int64,2}: 1 11 21 31 41 51 61 71 81 91 2 12 22 32 42 52 62 72 82 92 3 13 23 33 43 53 63 73 83 93 4 14 24 34 44 54 64 74 84 94 5 15 25 35 45 55 65 75 85 95 6 16 26 36 46 56 66 76 86 96 7 17 27 37 47 57 67 77 87 97 8 18 28 38 48 58 68 78 88 98 9 19 29 39 49 59 69 79 89 99 10 20 30 40 50 60 70 80 90 100  julia> for n in a print(n, " ") end 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100  您可以使用 = 代替 in. 迭代数组并更新 当您在数组上迭代时，每次循环中都会检查数组，以防它发生更改。一个你应该避免犯的错误是使用 push! 使数组在循环中增长。仔细运行以下文本，当您看到足够多的内容时准备使用 Ctrl-C(否则您的计算机最终会崩溃)： julia> c = [1] 1-element Array{Int64,1}: 1 julia> for i in c push!(c, i) @show c sleep(1) end c = [1,1] c = [1,1,1] c = [1,1,1,1] ...  循环变量和作用域 遍历每个项目的变量，即“循环变量”，仅存在于循环内部，并在循环结束后立即消失。 julia> for i in 1:10 @show i end i = 1 i = 2 i = 3 i = 4 i = 5 i = 6 i = 7 i = 8 i = 9 i = 10 julia> i ERROR: UndefVarError: i not defined  如果要记住循环之外的循环变量的值(例如，如果必须退出循环并需要知道所达到的值)，请使用 global 关键字定义一个比循环更持久的变量。 julia> for i in 1:10 global howfar if i % 4 == 0 howfar = i end end  julia> howfar 8  在这里，howfar 在循环之前是不存在的，但是当循环结束时，它存活了下来，讲述了它的故事。如果 howfar 在循环开始之前已存在，则仅当在循环中使用全局时才能更改其值。 在REPL中工作与在函数中编写代码略有不同。在函数中，您可以这样写： function f() howfar = 0 for i in 1:10 if i % 4 == 0 howfar = i end end return howfar end @show f()  8 循环中声明的变量 类似地，如果您在循环中声明一个新变量，则该变量在循环完成后将不复存在。在此示例中，k 是在以下位置创建的： julia> for i in 1:5 k = i^2 println("$(i) squared is $(k)") end  1 squared is 1 2 squared is 4 3 squared is 9 4 squared is 16 5 squared is 25  因此它在循环完成后不存在： julia> k ERROR: UndefVarError: k not defined  在循环的一个迭代中创建的变量在每次迭代结束时都会被忘记。在此循环中： for i in 1:10 z = i println("z is$z")
end

z is 1
z is 2
z is 3
z is 4
z is 5
z is 6
z is 7
z is 8
z is 9
z is 10

julia> counter = 0
0

julia> for i in 1:10
global counter
counter += i
end

julia> counter
55


for i in 1:10
if ! @isdefined z
println("z isn't defined")
end
z = i
println("z is $z") end  也许您只期望第一个循环产生“z未定义的错误”？事实上，即使 z 是在循环体中创建的，在下一次迭代开始时也没有定义它。 z isn't defined z is 1 z isn't defined z is 2 z isn't defined z is 3 z isn't defined z is 4 z isn't defined z is 5 z isn't defined z is 6 z isn't defined z is 7 z isn't defined z is 8 z isn't defined z is 9 z isn't defined z is 10  再说一遍，使用 global 关键字强制 z 在创建后在循环之外可用： for i in 1:10 global z if ! @isdefined z println("z isn't defined") else println("z was$z")
end
z = i
println("z is $z") end  z isn't defined z is 1 z was 1 z is 2 z was 2 ... z is 9 z was 9 z is 10 如果您在全局范围内工作，虽然 z 现在任何地方都可用，值为10。 although, if you're working in global scope, z is now available everywhere, with the value 10. 这种行为是因为我们在 REPL 工作。通常更好的做法是将代码放在函数中，在函数中不需要将从循环外部继承的变量标记为全局变量： function f() counter = 0 for i in 1:10 counter += i end return counter end  julia> f() 55  循环微调：continue 有时，在特定的迭代中，您可能希望跳到下一个值。您可以使用 continue 跳过循环中的其余代码，并使用下一个值再次启动循环。 for i in 1:10 if i % 3 == 0 continue end println(i) # this and subsequent lines are # skipped if i is a multiple of 3 end 1 2 4 5 7 8 10  推导 这一概念只是产生和收集项目的一种方式。在数学界，你会这样说： "设S是所有元素n的集合，其中n大于或等于1，小于或等于10。"  在Julia中，您可以这样写： julia> S = Set([n for n in 1:10]) Set([7,4,9,10,2,3,5,8,6,1])  [n for n in 1:10] 的结构被称为 数组推导列表推导 ("comprehension"，意思是“得到一切”，而不是“理解”)。外面的方括号将通过 计算放置在 for 迭代之前的表达式 而生成的元素集合在一起。而不是end ，使用方括号完成。 julia> [i^2 for i in 1:10] 10-element Array{Int64,1}: 1 4 9 16 25 36 49 64 81 100  可以指定元素的类型： julia> Complex[i^2 for i in 1:10] 10-element Array{Complex,1}: 1.0+0.0im 4.0+0.0im 9.0+0.0im 16.0+0.0im 25.0+0.0im 36.0+0.0im 49.0+0.0im 64.0+0.0im 81.0+0.0im 100.0+0.0im  但是 Julia 可以计算出你所产生的结果的类型： julia> [(i, sqrt(i)) for i in 1:10] 10-element Array{Tuple{Int64,Float64},1}: (1,1.0) (2,1.41421) (3,1.73205) (4,2.0) (5,2.23607) (6,2.44949) (7,2.64575) (8,2.82843) (9,3.0) (10,3.16228)  下面是如何通过推导来生成字典： julia> Dict(string(Char(i + 64)) => i for i in 1:26) Dict{String,Int64} with 26 entries: "Z" => 26 "Q" => 17 "W" => 23 "T" => 20 "C" => 3 "P" => 16 "V" => 22 "L" => 12 "O" => 15 "B" => 2 "M" => 13 "N" => 14 "H" => 8 "A" => 1 "X" => 24 "D" => 4 "G" => 7 "E" => 5 "Y" => 25 "I" => 9 "J" => 10 "S" => 19 "U" => 21 "K" => 11 "R" => 18 "F" => 6  接下来，这里是一个理解中的两个迭代器，用逗号分隔，这使得生成表变得非常容易。在这里，我们将创建一个元组表格： julia> [(r,c) for r in 1:5, c in 1:2] 5×2 Array{Tuple{Int64,Int64},2}: (1,1) (1,2) (2,1) (2,2) (3,1) (3,2) (4,1) (4,2) (5,1) (5,2)  r 经过五个循环，每个 c 的值对应一个循环。 嵌套循环 的工作方式正好相反。此处将遵守列主次顺序，如阵列中填充了纳秒时间值时所示： julia> [Int(time_ns()) for r in 1:5, c in 1:2] 5×2 Array{Int64,2}: 1223184391741562 1223184391742642 1223184391741885 1223184391742817 1223184391742067 1223184391743009 1223184391742256 1223184391743184 1223184391742443 1223184391743372  您还可以提供一个测试表达式来过滤产品。例如，生成 1 到 100 之间可被 7 整除的所有整数： julia> [x for x in 1:100 if x % 7 == 0] 14-element Array{Int64,1}: 7 14 21 28 35 42 49 56 63 70 77 84 91 98  生成表达式 与推导类似，生成器表达式可用于通过迭代变量来生成值，但与理解不同的是，这些值是按需生成的。 julia> sum(x^2 for x in 1:10) 385  julia> collect(x for x in 1:100 if x % 7 == 0) 14-element Array{Int64,1}: 7 14 21 28 35 42 49 56 63 70 77 84 91 98  枚举数组 通常，您希望逐个元素检查数组元素，同时跟踪每个元素的索引号。enumerate() 函数的作用是：生成一个索引号和每个索引号的值，从而为您提供一个可迭代版本的内容： julia> m = rand(0:9, 3, 3) 3×3 Array{Int64,2}: 6 5 3 4 0 7 1 7 4 julia> [i for i in enumerate(m)] 3×3 Array{Tuple{Int64,Int64},2}: (1, 6) (4, 5) (7, 3) (2, 4) (5, 0) (8, 7) (3, 1) (6, 7) (9, 4)  在循环的每次迭代中检查数组是否可能发生更改。 数组压缩 有时，您希望同时处理两个或更多个数组，先取每个数组的第一个元素，然后再取第二个，依此类推。使用名字挺好的 zip() 函数可以做到这一点： julia> for i in zip(0:10, 100:110, 200:210) println(i) end  (0,100,200) (1,101,201) (2,102,202) (3,103,203) (4,104,204) (5,105,205) (6,106,206) (7,107,207) (8,108,208) (9,109,209) (10,110,210)  如果数组的大小不同，你会认为它们都会出错。如果第三个数组太大或太小怎么办？ julia> for i in zip(0:10, 100:110, 200:215) println(i) end (0,100,200) (1,101,201) (2,102,202) (3,103,203) (4,104,204) (5,105,205) (6,106,206) (7,107,207) (8,108,208) (9,109,209) (10,110,210)  但 Julia 没有被耍-任何一个数组中的任何一个供过于求或供过于求都会得到很好的处理。 julia> for i in zip(0:15, 100:110, 200:210) println(i) end (0,100,200) (1,101,201) (2,102,202) (3,103,203) (4,104,204) (5,105,205) (6,106,206) (7,107,207) (8,108,208) (9,109,209) (10,110,210)  但是，这在填充数组的情况下不起作用，在这种情况下，维度必须匹配： (v1.0) julia> [i for i in zip(0:4, 100:102, 200:202)] ERROR: DimensionMismatch("dimensions must match") Stacktrace: [1] promote_shape at ./indices.jl:129 [inlined] [2] axes(::Base.Iterators.Zip{UnitRange{Int64},Base.Iterators.Zip2{UnitRange{Int64},UnitRange{Int64}}}) at ./iterators.jl:371 [3] _array_for at ./array.jl:611 [inlined] [4] collect(::Base.Generator{Base.Iterators.Zip{UnitRange{Int64},Base.Iterators.Zip2{UnitRange{Int64},UnitRange{Int64}}},getfield(Main, Symbol("##5#6"))}) at ./array.jl:624 [5] top-level scope at none:0  (v1.0) julia> [i for i in zip(0:2, 100:102, 200:202)] 3-element Array{Tuple{Int64,Int64,Int64},1}: (0, 100, 200) (1, 101, 201) (2, 102, 202)  可迭代对象 对于可以遍历的所有内容，“For Something in Something”结构都是一样的：数组、字典、字符串、集合、范围等等。在Julia中，这是一个普遍的原则：有许多方法可以创建“可迭代对象”，该对象被设计为作为迭代过程的一部分，一次提供一个元素。 我们已经遇到的最明显的例子是Range对象。当您在REPL中键入它时，它看起来并不多： julia> ro = 0:2:100 0:2:100  但是当你开始遍历它时，它会给出数字： julia> [i for i in ro] 51-element Array{Int64,1}: 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 ⋮ 74 76 78 80 82 84 86 88 90 92 94 96 98 100  如果需要数组中某个范围(或其他可迭代对象)中的数字，可以使用 collect()将其收集起来： julia> collect(0:25:100) 5-element Array{Int64,1}: 0 25 50 75 100  您不必收集可迭代对象的每个元素，只需对其进行迭代即可。当您有其他Julia函数创建的可迭代对象时，这一点特别有用。例如，permutations()创建一个可迭代对象，该对象包含数组的所有排列。当然，您可以使用collect()获取它们并创建一个新数组： julia> collect(permutations(1:4)) 24-element Array{Array{Int64,1},1}: [1,2,3,4] [1,2,4,3] … [4,3,2,1]  但是在任何大的东西上，都有成百上千的排列。这就是为什么迭代器对象不能同时从迭代中产生所有值的原因：内存和性能。Range 对象不会占用太多空间，即使遍历它可能需要很长时间，具体取决于范围有多大。如果您一次生成所有的数字，而不是仅在需要时才生成它们，那么它们都必须存储在某个地方，直到您需要使用它们… Julia为处理其他类型的数据提供了可迭代的对象。例如，在处理文件时，可以将打开的文件视为可迭代对象：  filehandle = "/Users/me/.julia/logs/repl_history.jl" for line in eachline(filehandle) println(length(line), line) end  使用 eachindex() 迭代数组时的常见模式是对 i 的每个值执行某些任务，其中 i 是每个元素的索引号，而不是元素：  for i = 1:length(A) # do something with i or A[i] end  有一种更有效(即在某些情况下更快)执行此操作的方法：  for i in eachindex(A) # do something with i or A[i] end  更多迭代器 有一个名为 IterTools.jl 的 Julia 包，它提供了一些高级迭代器函数。 (v1.0) pkg> ] add IterTools julia> using IterTools  例如，partition()将迭代器中的对象分组到易于处理的块中： julia> collect(partition(1:10, 3, 1)) 8-element Array{Tuple{Int64,Int64,Int64},1}: (1, 2, 3) (2, 3, 4) (3, 4, 5) (4, 5, 6) (5, 6, 7) (6, 7, 8) (7, 8, 9) (8, 9, 10)  chain() 一个接一个地处理所有迭代器：  for i in chain(1:3, ['a', 'b', 'c']) @show i end i = 1 i = 2 i = 3 i = 'a' i = 'b' i = 'c'  subsets() 处理对象的所有子集。可以指定大小：  for i in subsets(collect(1:6), 3) @show i end i = [1,2,3] i = [1,2,4] i = [1,2,5] i = [1,2,6] i = [1,3,4] i = [1,3,5] i = [1,3,6] i = [1,4,5] i = [1,4,6] i = [1,5,6] i = [2,3,4] i = [2,3,5] i = [2,3,6] i = [2,4,5] i = [2,4,6] i = [2,5,6] i = [3,4,5] i = [3,4,6] i = [3,5,6] i = [4,5,6]  嵌套循环 如果希望将一个循环嵌套在另一个循环中，则不必复制 forend 关键字。只要用逗号： julia> for x in 1:10, y in 1:10 @show (x, y) end (x,y) = (1,1) (x,y) = (1,2) (x,y) = (1,3) (x,y) = (1,4) (x,y) = (1,5) (x,y) = (1,6) (x,y) = (1,7) (x,y) = (1,8) (x,y) = (1,9) (x,y) = (1,10) (x,y) = (2,1) (x,y) = (2,2) (x,y) = (2,3) (x,y) = (2,4) (x,y) = (2,5) (x,y) = (2,6) (x,y) = (2,7) (x,y) = (2,8) (x,y) = (2,9) (x,y) = (2,10) (x,y) = (3,1) (x,y) = (3,2) ... (x,y) = (9,9) (x,y) = (9,10) (x,y) = (10,1) (x,y) = (10,2) (x,y) = (10,3) (x,y) = (10,4) (x,y) = (10,5) (x,y) = (10,6) (x,y) = (10,7) (x,y) = (10,8) (x,y) = (10,9) (x,y) = (10,10)  (十分有用的@show 宏打印出东西的名称及其值。) 较短和较长形式的嵌套循环之间的一个不同之处是 break julia> for x in 1:10 for y in 1:10 @show (x, y) if y % 3 == 0 break end end end (x,y) = (1,1) (x,y) = (1,2) (x,y) = (1,3) (x,y) = (2,1) (x,y) = (2,2) (x,y) = (2,3) (x,y) = (3,1) (x,y) = (3,2) (x,y) = (3,3) (x,y) = (4,1) (x,y) = (4,2) (x,y) = (4,3) (x,y) = (5,1) (x,y) = (5,2) (x,y) = (5,3) (x,y) = (6,1) (x,y) = (6,2) (x,y) = (6,3) (x,y) = (7,1) (x,y) = (7,2) (x,y) = (7,3) (x,y) = (8,1) (x,y) = (8,2) (x,y) = (8,3) (x,y) = (9,1) (x,y) = (9,2) (x,y) = (9,3) (x,y) = (10,1) (x,y) = (10,2) (x,y) = (10,3) julia> for x in 1:10, y in 1:10 @show (x, y) if y % 3 == 0 break end end (x,y) = (1,1) (x,y) = (1,2) (x,y) = (1,3)  请注意，break以较短的形式出现在内部循环和外部循环中，而在较长的形式中仅出现在内部循环中。 优化嵌套循环 对于Julia，内部循环应该关注行而不是列。这是由于数组如何存储在内存中。例如，在这个Julia数组中，单元格1、2、3和4彼此相邻存储在内存中(“列优先”格式)。因此，从1到2到3向下移动列比沿行移动要快，因为从列到列从1到5到9跳过需要额外的计算： +-----+-----+-----+--+ | 1 | 5 | 9 | | | | | +--------------------+ | 2 | 6 | 10 | | | | | +--------------------+ | 3 | 7 | 11 | | | | | +--------------------+ | 4 | 8 | 12 | | | | | +-----+-----+-----+--+  下面的示例由简单的循环组成，但是行和列的迭代方式不同。“不好”的版本逐列查看第一行，然后向下移动到下一行，依此类推。 function laplacian_bad(lap_x::Array{Float64,2}, x::Array{Float64,2}) nr, nc = size(x) for ir = 2:nr-1, ic = 2:nc-1 # bad loop nesting order lap_x[ir, ic] = (x[ir+1, ic] + x[ir-1, ic] + x[ir, ic+1] + x[ir, ic-1]) - 4*x[ir, ic] end end  在“好的”版本中，两个循环被正确嵌套，以便内部循环按照数组的内存布局在行中向下移动： function laplacian_good(lap_x::Array{Float64,2}, x::Array{Float64,2}) nr,nc = size(x) for ic = 2:nc-1, ir = 2:nr-1 # good loop nesting order lap_x[ir,ic] = (x[ir+1,ic] + x[ir-1,ic] + x[ir,ic+1] + x[ir,ic-1]) - 4*x[ir,ic] end end  另一种提高速度的方法是使用宏@inbounds删除数组边界检查： function laplacian_good_nocheck(lap_x::Array{Float64,2}, x::Array{Float64,2}) nr,nc = size(x) for ic = 2:nc-1, ir = 2:nr-1 # good loop nesting order @inbounds begin lap_x[ir,ic] = # no array bounds checking (x[ir+1,ic] + x[ir-1,ic] + x[ir,ic+1] + x[ir,ic-1]) - 4*x[ir,ic] end end end  下面是测试函数： function main_test(nr, nc) field = zeros(nr, nc) for ic = 1:nc, ir = 1:nr if ir == 1 || ic == 1 || ir == nr || ic == nc field[ir,ic] = 1.0 end end lap_field = zeros(size(field)) t = @elapsed laplacian_bad(lap_field, field) println(rpad("laplacian_bad", 30), t) t = @elapsed laplacian_good(lap_field, field) println(rpad("laplacian_good", 30), t) t = @elapsed laplacian_good_nocheck(lap_field, field) println(rpad("laplacian_good no check", 30), t) end  结果表明，仅根据行/列扫描顺序就可以得到不同的性能差异。“不检查”的版本更快.。 julia> main_test(10000,10000) laplacian_bad 1.947936034 laplacian_good 0.190697149 laplacian_good no check 0.092164871  创建自用的可迭代对象 可以设计自己的可迭代对象。定义类型时，需要向 Julia 的 iterate()函数添加几个方法。那你就可以用类似于for .. end循环用于处理对象的组件，这些iterate()方法将在必要时自动调用。 下面的示例演示如何创建一个可迭代对象，该对象生成将大写字母与1到9之间的数字组合在一起的字符串序列。因此，我们的序列中的第一个项目是“A1”，然后是“A2”、“A3”，直到“A9”，然后是“B1”、“B2”，等等，最后是“Z9”。 首先，我们将定义一个名为 SN (StringNumber)的新类型： mutable struct SN str::String num::Int64 end  稍后，我们将使用如下内容创建此类型的可迭代对象： sn = SN("A", 1)  迭代器将生成到“Z9”的所有字符串。 现在，我们必须向 iterate()函数添加两个方法。这个函数已经存在于 Julia 中(这就是为什么您可以迭代所有的基本数据对象)，需要Base前缀：我们将向现有的iterate()函数添加一个新的用于处理这些特殊对象的方法。 第一个方法不接受参数(类型除外)，用于启动迭代过程。 function Base.iterate(sn::SN) str = sn.str num = sn.num if num == 9 nextnum = 1 nextstr = string(Char(Int(str[1])) + 1) else nextnum = num + 1 nextstr = str end return (sn, SN(nextstr, nextnum)) end  这将返回一个元组：我们计算过的迭代器的第一个值和未来的值(以防万一我们希望在“A1”以外的位置启动迭代器)。 iterate()的第二个方法接受两个参数：可迭代对象和当前状态。它再次返回包含两个值的元组，即下一项和下一状态。但是首先，如果没有更多的值可用，iterate()函数应该什么也不会返回。 function Base.iterate(sn::SN, state) # check if we've finished? if state.str == "[" # when Z changes to [ we're done return end # we haven't finished, so we'll use the incoming one immediately str = state.str num = state.num # and prepare the one after that, to be saved for later if num == 9 nextnum = 1 nextstr = string(Char(Int(str[1])) + 1) else nextnum = num + 1 nextstr = state.str end # return: the one to use next, the one after that return (SN(str, num), SN(nextstr, nextnum)) end  告诉迭代器什么时候完成很简单，因为一旦传入状态包含“[”我们已经完成了，因为“[”(91)的代码紧跟在“Z”(90)的代码之后。 通过添加这两个方法来处理 SN 类型，现在可以对它们进行迭代。为其他几个 Base 函数添加一些方法也很有用，例如show()length()length()方法计算出从 sn 开始还有多少 SN 字符串可用。 Base.show(io::IO, sn::SN) = print(io, string(sn.str, sn.num)) function Base.length(sn::SN) cn1 = Char(Int(Char(sn.str[1]) + 1)) cnz = Char(Int(Char('Z'))) (length(cn1:cnz) * 9) + (10 - sn.num) end  迭代器现在可以使用了： julia> sn = SN("A", 1) A1 julia> for i in sn @show i end  i = A1 i = A2 i = A3 i = A4 i = A5 i = A6 i = A7 i = A8 ... i = Z6 i = Z7 i = Z8 i = Z9  julia> for sn in SN("K", 9) print(sn, " ") end  K9 L1 L2 L3 L4 L5 L6 L7 L8 L9 M1 M2 M3 M4 M5 M6 M7 M8 M9 N1 N2 N3 N4 N5 N6 N7 N8 N9 O1 O2 O3 O4 O5 O6 O7 O8 O9 P1 P2 P3 P4 P5 P6 P7 P8 P9 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 R1 R2 R3 R4 R5 R6 R7 R8 R9 S1 S2 S3 S4 S5 S6 S7 S8 S9 T1 T2 T3 T4 T5 T6 T7 T8 T9 U1 U2 U3 U4 U5 U6 U7 U8 U9 V1 V2 V3 V4 V5 V6 V7 V8 V9 W1 W2 W3 W4 W5 W6 W7 W8 W9 X1 X2 X3 X4 X5 X6 X7 X8 X9 Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8 Y9 Z1 Z2 Z3 Z4 Z5 Z6 Z7 Z8 Z9 julia> collect(SN("Q", 7)), (Any[Q7, Q8, Q9, R1, R2, R3, R4, R5, R6, R7 … Y9, Z1, Z2, Z3, Z4, Z5, Z6, Z7, Z8, Z9],)  While 循环 若要在条件为真时重复某些表达式，请使用while ... end构造。 julia> x = 0 0 julia> while x < 4 println(x) global x += 1 end 0 1 2 3  如果在函数外部工作，则需要 global 声明 x，然后才能更改其值。在函数内部，不需要global 如果希望在语句之后(而不是在语句之前)测试条件，生成“do...until”的形式，请使用以下结构： while true println(x) x += 1 x >= 4 && break end 0 1 2 3  这里我们使用的是布尔转换表达式而不是if ... end语句。 使用Julia的宏，您可以创建自己的控制结构。请参见 Metaprogramming 元编程。 异常 如果要编写检查错误并妥善处理这些错误的代码，请使用try ... catch结构。 使用catch短语，您可以处理代码中出现的问题，这可能会使程序继续运行，而不是陷入停顿。 在下一个示例中，我们的代码试图直接更改字符串的第一个字符(这是不允许的，因为Julia中的字符串不能就地修改)： julia> s = "string"; julia> try s[1] = "p" catch e println("caught an error:$e")
println("but we can continue with execution...")
end

caught an error: MethodError(setindex!,("string","p",1)) but we can continue with execution...


error()函数使用给定的消息引发错误异常。

Do block

julia> smallprimes = [1,2,3,5,7,11,13,17,19,23];

julia> findall(x -> isequal(13, x), smallprimes)
1-element Array{Int64,1}:
7


anonymous function 匿名函数 (x -> isequal(13, x)find()的第一个参数，它对第二个参数进行操作。但是有了do代码块，你就可以把函数拿出来放在do之间……

julia> findall(smallprimes) do x
isequal(x, 13)
end
1-element Array{Int64,1}:
7


 « Introducing JuliaControlling the flow » Types Functions

Functions

 « Introducing JuliaFunctions » Controlling the flow Dictionaries and sets

函数

• 函数包含单个表达式
• 函数包含多个表达式
• 函数不需要名字

单表达式函数

julia> f(x) = x * x
f (generic function with 1 method)

julia> f(2)
4

julia> g(x, y) = sqrt(x^2 + y^2)
g (generic function with 1 method)

julia> g(3,4)
5.0


具有多个表达式的函数

function functionname(arg1, arg2)
expression
expression
expression
...
expression
end


function breakfast()
maketoast()
brewcoffee()
end

breakfast (generic function with 1 method)


julia> function paybills(bankbalance)
if bankbalance < 0
return false
else
return true
end
end
paybills (generic function with 1 method)

julia> payBills(20)
true

julia> payBills(-10)
false


Some consider it good style to always use a return statement, even if it's not strictly necessary. Later we'll see how to make sure that the function doesn't go adrift if you call it with the wrong type of argument.

多值返回

To return more than one value from a function, use a tuple.

function doublesix()
return (6, 6)
end
doublesix (generic function with 1 method)

julia> doublesix()
(6, 6)


Here you could write 6, 6 without parentheses.

可选参数和可变数量的参数

You can define functions with optional arguments, so that the function can use sensible defaults if specific values aren't supplied. You provide a default symbol and value in the argument list:

function xyzpos(x, y, z=0)
println("$x,$y, $z") end xyzpos (generic function with 2 methods)  And when you call this function, if you don't provide a third value, the variable z defaults to 0 and uses that value inside the function. julia> xyzpos(1,2) 1, 2, 0 julia> xyzpos(1,2,3) 1, 2, 3  关键字参数与位置参数 When you write a function with a long list of arguments like this: function f(p, q, r, s, t, u) ... end  早晚你会可能忘记他们的顺序。 sooner or later, you will forget the order in which you have to supply the arguments. For instance, it can be: f("42", -2.123, atan2, "obliquity", 42, 'x')  or f(-2.123, 42, 'x', "42", "obliquity", atan2)  You can avoid this problem by using keywords to label arguments. Use a semicolon after the function's unlabelled arguments, and follow it with one or more keyword=value pairs: function f(p, q ; r = 4, s = "hello") println("p is$p")
println("q is $q") return "r =>$r, s => $s" end f (generic function with 1 method)  When called, this function expects two arguments, and also accepts a number and a string, labelled r and s. If you don't supply the keyword arguments, their default values are used: julia> f(1,2) p is 1 q is 2 "r => 4, s => hello" julia> f("a", "b", r=pi, s=22//7) p is a q is b "r => π = 3.1415926535897..., s => 22//7"  If you supply a keyword argument, it can be anywhere in the argument list, not just at the end or in the matching place. julia> f(r=999, 1, 2) p is 1 q is 2 "r => 999, s => hello" julia> f(s="hello world", r=999, 1, 2) p is 1 q is 2 "r => 999, s => hello world" julia>  When defining a function with keyword arguments, remember to insert a semicolon before the keyword/value pairs. Here's another example from the Julia manual. The rtol keyword can appear anywhere in the list of arguments or it can be omitted: julia> isapprox(3.0, 3.01, rtol=0.1) true julia> isapprox(rtol=0.1, 3.0, 3.01) true julia> isapprox(3.0, 3.00001) true  A function definition can combine all the different kinds of arguments. Here's one with normal, optional, and keyword arguments: function f(a1, opta2=2; key="foo") println("normal argument:$a1")
println("optional argument: $opta2") println("keyword argument:$key")
end
f (generic function with 2 methods)

julia> f(1)
normal argument: 1
optional argument: 2
keyword argument: foo

julia> f(key=3, 1)
normal argument: 1
optional argument: 2
keyword argument: 3

julia> f(key=3, 2, 1)
normal argument: 2
optional argument: 1
keyword argument: 3


Functions with variable number of arguments

Functions can be defined so that they can accept any number of arguments:

function fvar(args...)
println("you supplied $(length(args)) arguments") for arg in args println(" argument ", arg) end end  The three dots indicate the famous splat. Here it means 'any', including 'none'. You can call this function with any number of arguments: julia> fvar() you supplied 0 arguments julia> fvar(64) you supplied 1 arguments argument 64 julia> fvar(64,65) you supplied 2 arguments argument 64 argument 65 julia> fvar(64,65,66) you supplied 3 arguments argument 64 argument 65 argument 66  and so on. Here's another example. Suppose you define a function that accepts two arguments: function test(x, y) println("x$x y $y") end  You can call this in the usual way: julia> test(12, 34) x 12 y 34  If you have the two numbers, but in a tuple, then how can you supply a single tuple of numbers to this two argument function? Again, the answer is to use the ellipsis (splat). julia> test((12, 34) ...) x 12 y 34  The use of the ellipsis or 'splat' is also referred to as 'splicing' the arguments: julia> test([3,4]...) x 3 y 4  You can also do this: julia> map(test, [3, 4]...) x 3 y 4  局部变量与改变参数的值 Any variable you define inside a function will be forgotten when the function finishes. function test(a,b,c) subtotal = a + b + c end  julia> test(1,2,3) 6 julia> subtotal LoadError: UndefVarError: subtotal not defined  If you want to keep values around across function calls, then you can think about using global variables. A function can't modify an existing variable passed to it as an argument, but it can change the contents of a container passed to it. For example, here is a function that changes its argument to 5: function set_to_5(x) x = 5 end  julia> x = 3 3 julia> set_to_5(x) 5 julia> x 3  Although the x inside the function is changed, the x outside the function isn't. Variable names in functions are local to the function. But a function can modify the contents of a container, such as an array. This function uses the [:] syntax to access the contents of the container x, rather than change the value of the variable x: function fill_with_5(x) x[:] .= 5 end  julia> x = collect(1:10); julia> fill_with_5(x) 5 julia> x 10-element Array{Int64,1}: 5 5 5 5 5 5 5 5 5 5  You can change elements of the array, but you can't change the variable so that it points to a different array. In other words, your function isn't allowed to change the binding of the argument. 匿名函数 Sometimes you don't want to worry about thinking up a cool name for a function. Anonymous functions — functions with no name — can be used in a number of places in Julia, such as with map(), and in list comprehensions. The syntax uses ->, like this: x -> x^2 + 2x - 1  which defines a nameless function that takes a argument, calls it x, and returns x^2 + 2x - 1. For example, the first argument of the map() function is a function, and you can define an one-off function that exists just for one particular map() operation: julia> map(x -> x^2 + 2x - 1, [1,3,-1]) 3-element Array{Int64,1}: 2 14 -2  After the map() finishes, both the function and the argument x have disappeared: julia> x ERROR: x not defined  If you want an anonymous function that accepts more than one argument, provide the arguments as a tuple: julia> map((x,y,z) -> x + y + z, [1,2,3], [4, 5, 6], [7, 8, 9]) 3-element Array{Int64,1}: 12 15 18  Notice that the results are 12, 15, 18, rather than 6, 15, and 24. The anonymous function takes the first value of each of the three arrays and adds them, followed by the second, then the third. In addition, anonymous functions can have zero arguments, if you use an 'empty' tuple(): julia> random = () -> rand(0:10) #3 (generic function with 1 method) julia> random() 3  julia> random() 1  Map 如果已有一个函数和数组，你可以通过使用 map() 对数组中的每个元素来调用函数。这将会依次对每个元素进行调用并收集结果，返回一个数组。这个过程称之为 映射 (mapping): julia> a=1:10; julia> map(sin, a) 10-element Array{Float64,1}: 0.841471 0.909297 0.14112 -0.756802 -0.958924 -0.279415 0.656987 0.989358 0.412118 -0.544021  map() 返回一个新的数组，但调用 map!() 则会修改原始数组的内容。 通常，不必用 map() 来对数组的每个成员都调用 sin() 函数，因为很多函数本身就自动支持 "元素级别" 的操作, 两种版本的耗时是相当的（ 用 sin.() 可能还有优势，取决于数据规模）: julia> @time map(sin, 1:10000); 0.149156 seconds (568.96 k allocations: 29.084 MiB, 2.01% gc time) julia> @time sin.(1:10000); 0.074661 seconds (258.76 k allocations: 13.086 MiB, 5.86% gc time)  map() 会收集结果然后返回一个数组，优势你只是想要 '映射' 操作但并不需要返回一个数组的结果。这样的话，用 foreach(): julia> foreach(println, 1:20) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  此时 ans 是 nothing (ans == nothing 返回 true). 多个数组映射 你可以对不止一个数组进行 map() 操作. The function is applied to the first element of each of the arrays, then to the second, and so on. The arrays must be of the same length (unlike the zip() function, which is more tolerant). Here's an example which generates an array of imperial (non-metric) spanner/socket sizes. The second array is just a bunch of repeated 32s to match the integers from 5 to 24 in the first array. Julia simplifies the rationals for us: julia> map(//, 5:24, fill(32,20)) 20-element Array{Rational{Int64},1}: 5//32 3//16 7//32 1//4 9//32 5//16 11//32 3//8 13//32 7//16 15//32 1//2 17//32 9//16 19//32 5//8 21//32 11//16 23//32 3//4  (In reality, an imperial spanner set won't contain some of these strange sizes - I've never seen an old 17/32" spanner, but you can buy them online.) 使用 . 语法来调用函数 除了 map()以外, it's possible to apply functions directly to arguments that are arrays. See the section on Broadcasting: dot syntax for vectorizing functions. Reduce 和 folding The map() function collects the results of some function working on each and every element of an iterable object, such as an array of numbers. The reduce() function does a similar job, but after every element has been seen and processed by the function, only one is left. The function should take two arguments and return one. The array is reduced by continual application, so that just one is left. A simple example is the use of reduce() to sum the numbers in an iterable object (which works like the built-in function sum()): julia> reduce(+, 1:10) 55  Internally, this does something similar to this: ((((((((1 + 2) + 3) + 4) + 6) + 7) + 8) + 9) + 10)  After each operation adding two numbers, a single number is carried over to the next iteration. This process reduces all the numbers to a single final result. A more useful example is when you want to apply a function to work on each consecutive pair in an iterable object. For example, here's a function that compares the length of two strings and returns the longer one: julia> l(a, b) = length(a) > length(b) ? a : b l (generic function with 1 method)  This can be used to find the longest word in a sentence by working through the string, pair by pair: julia> reduce(l, split("This is a sentence containing some very long strings")) "containing"  "This" lasts a few rounds, and is then beaten by "sentence", but finally "containing" takes the lead, and there are no other challengers after that. If you want to see the magic happen, redefine l like this: julia> l(a, b) = (println("comparing \"$a\" and \"$b\""); length(a) > length(b) ? a : b) l (generic function with 1 method) julia> reduce(l, split("This is a sentence containing some very long strings")) comparing "This" and "is" comparing "This" and "a" comparing "This" and "sentence" comparing "sentence" and "containing" comparing "containing" and "some" comparing "containing" and "very" comparing "containing" and "long" comparing "containing" and "strings" "containing"  You can use an anonymous function to process an array pairwise. The trick is to make the function leave behind a value that will be used for the next iteration. This code takes an array such as [1, 2, 3, 4, 5, 6...] and returns [1 * 2, 2 * 3, 3 * 4, 4 * 5...], multiplying adjacent elements. store = Int[]; reduce((x,y) -> (push!(store, x * y); y), 1:10)  julia> store 9-element Array{Int64,1}: 2 6 12 20 30 42 56 72 90  Folding Julia also offers two related functions, foldl() and foldr(). These offer the same basic functionality as reduce(). The differences are concerned with the direction in which the traversal occurs. In the simple summation example above, our best guess at what happened inside the reduce() operation assumed that the first pair of elements were added first, followed by the second pair, and so on. However, it's also possible that reduce() started at the end and worked towards the front. If it's important, use foldl() for left to right, and foldr() for right to left. In many cases, the results are the same, but here's an example where you'll get different results depending on which version you'll use: julia> reduce(-, 1:10) -53 julia> foldl(-, 1:10) -53 julia> foldr(-, 1:10) -5  Julia offers other functions in this group: check out mapreduce(), mapfoldl(), and mapfoldr(). If you want to use reduce() and the fold-() functions for functions that take only one argument, use a dummy second argument: julia> reduce((x, y) -> sqrt(x), 1:4, init=256) 1.4142135623730951  which is equivalent to calling the sqrt() function four times: julia> sqrt(sqrt(sqrt(sqrt(256)))) 1.4142135623730951  返回函数（柯里化） You can treat Julia functions in the same way as any other Julia object, particularly when it comes to returning them as the result of other functions. For example, let's create a function-making function. Inside this function, a function called newfunction is created, and this will raise its argument (y) to the number that was originally passed in as the argument x. This new function is returned as the value of the create_exponent_function() function. function create_exponent_function(x) newfunction = function (y) return y^x end return newfunction end  Now we can construct lots of exponent-making functions. First, let's build a squarer() function: julia> squarer = create_exponent_function(2) #8 (generic function with 1 method)  and a cuber() function: julia> cuber = create_exponent_function(3) #9 (generic function with 1 method)  While we're at it, let's do a "raise to the power of 4" function (called  quader, although I'm starting to struggle with the Latin and Greek naming): julia> quader = create_exponent_function(4) #10 (generic function with 1 method)  These are ordinary Julia functions: julia> squarer(4) 16 julia> cuber(5) 125 julia> quader(6) 1296  The definition of the create_exponent_function() above is perfectly valid Julia code, but it's not idiomatic. For one thing, the return value doesn't always need to be provided explicitly — the final evaluation is returned if return isn't used. Also, in this case, the full form of the function definition can be replaced with the shorter one-line version. This gives the concise version: function create_exponent_function(x) y -> y^x end  which acts in the same way. make_counter = function() so_far = 0 function() so_far += 1 end end  julia> a = make_counter(); julia> b = make_counter(); julia> a() 1 julia> a() 2 julia> a() 3 julia> a() 4 julia> b() 1 julia> b() 2  Here's another example of making functions. To make it easier to see what the code is doing, here is the make_counter() function written in a slightly different manner: function make_counter() so_far = 0 counter = function() so_far += 1 return so_far end return counter end  julia> a = make_counter() #15 (generic function with 1 method) julia> a() 1 julia> a() 2 julia> a() 3 julia> for i in 1:10 a() end julia> a() 14  函数链与组合 在 Julia 中函数可以互相组合。 函数组合是指将两个或多个函数应用于参数。你可以使用组合运算符 (∘) 来组合函数。（在 REPL 中使用\circ来打出运算符）。举个例子，sqrt()+ 函数组合在一起： julia> (sqrt ∘ +)(3, 5) 2.8284271247461903  先将数字相加，然后计算平方根。 下面这个例子复合了三个函数。 julia> map(first ∘ reverse ∘ uppercase, split("you can compose functions like this")) 6-element Array{Char,1}: 'U' 'N' 'E' 'S' 'E' 'S'  函数链（有时又称为“管道”或者说“用管道来将数据送到下一个函数”）是指将前一个函数的输出应用到后一个函数： julia> 1:10 |> sum |> sqrt 7.416198487095663  其中 sum() 的值 递给 sqrt() 函数。等价于： julia> (sqrt ∘ sum)(1:10) 7.416198487095663  管道能够给一个接受单参数的函数发送数据。如果函数需要多个参数，可以用匿名函数： julia> collect(1:9) |> n -> filter(isodd, n) 5-element Array{Int64,1}: 1 3 5 7 9  Methods A function can have one or more different methods of doing a similar job. Each method usually concentrates on doing the job for a particular type. Here is a function to check a longitude when you type in a location: function check_longitude_1(loc) if -180 < loc < 180 println("longitude$loc is a valid longitude")
else
println("longitude $loc should be between -180 and 180 degrees") end end check_longitude_1 (generic function with 1 method)  The message ("generic function with 1 method") you see if you define this in the REPL tells you that there is currently one way you can call the check_longitude_1() function. If you call this function and supply a number, it works fine. julia> check_longitude_1(-182) longitude -182 should be between -180 and 180 degrees julia> check_longitude_1(22) longitude 22 is a valid longitude  But what happens when you type in a longitude in, say, the format seen on Google Maps: julia> check_longitude_1("1°24'54.6\"W") ERROR: MethodError: isless has no method matching isless(::Int64, ::UTF8String)  The error tells us that the function has stopped because the concept of less than (<), which we are using inside our function, makes no sense if one argument is a string and the other a number. Strings are not less than or greater than integers because they are two different things, so the function fails at that point. Notice that the check_longitude_1() function did start executing, though. The argument loc could have been anything - a string, a floating point number, an integer, a symbol, or even an array. There are many ways for this function to fail. This is not the best way to write code! To fix this problem, we might be tempted to add code that tests the incoming value, so that strings are handled differently. But Julia proposes a better alternative: methods and multiple dispatch. In the case where the longitude is supplied as a numeric value, the loc argument is defined as 'being of type Real'. Let's start again, define a new function, and do it properly: function check_longitude(loc::Real) if -180 < loc < 180 println("longitude$loc is a valid longitude")
else
println("longitude $loc should be between -180 and 180 degrees") end end  Now this check_longitude function doesn't even run if the value in loc isn't a real number. The problems of what to do when the value is a string is avoided. With a type Real, this particular method can be called with any argument provided that it is some kind of number. We can use the applicable() function to test this. applicable() lets you know whether you can apply a function to an argument — i.e. whether there is an available method for the function for arguments with that type: julia> applicable(check_longitude, -30) true julia> applicable(check_longitude, pi) true julia> applicable(check_longitude, 22/7) true julia> applicable(check_longitude, 22//7) true julia> applicable(check_longitude, "1°24'54.6\"W") false  The false indicates that you can't pass a string value to the check_longitude() function because there is no method for this function that accepts a string: julia> check_longitude("1°24'54.6\"W") ERROR: MethodError: check_longitude has no method matching check_longitude(::UTF8String)  Now the body of the function isn't even looked at — Julia doesn't know a method for calling check_longitude() function with a string argument. The obvious next step is to add another method for the check_longitude() function, only this time one that accepts a string argument. In this way, a function can be given a number of alternative methods: one for numeric arguments, one for string arguments, and so on. Julia selects and runs one of the available methods depending on the types of arguments you provide to a function. This is multiple dispatch. function check_longitude(loc::String) # not real code, obviously! if endswith(loc, "W") println("longitude$loc is West of Greenwich")
else
println("longitude $loc is East of Greenwich") end end check_longitude (generic function with 2 methods)  Now the check_longitude() function has two methods. The code to run depends on the types of the arguments you provide to the function. And you can avoid testing the types of arguments at the start of this function, because Julia only dispatches the flow to the string-handling method if loc is a string. You can use the built-in methods() function to find out how many methods you have defined for a particular function. julia> methods(check_longitude) # 2 methods for generic function "check_longitude": check_longitude(loc::Real) at none:2 check_longitude(loc::String) at none:3  An instructive example is to see how many different methods the + function has: julia> methods(+) # 176 methods for generic function "+": [1] +(x::Bool, z::Complex{Bool}) in Base at complex.jl:276 [2] +(x::Bool, y::Bool) in Base at bool.jl:104 ... [174] +(J::LinearAlgebra.UniformScaling, B::BitArray{2}) in LinearAlgebra at /Users/osx/buildbot/slave/package_osx64/build/usr/share/julia/stdlib/v0.7/LinearAlgebra/src/uniformscaling.jl:90 [175] +(J::LinearAlgebra.UniformScaling, A::AbstractArray{T,2} where T) in LinearAlgebra at /Users/osx/buildbot/slave/package_osx64/build/usr/share/julia/stdlib/v0.7/LinearAlgebra/src/uniformscaling.jl:91 [176] +(a, b, c, xs...) in Base at operators.jl:466  This is a long list of every method currently defined for the + function; there are many different types of thing that you can add together, including arrays, matrices, and dates. If you design your own types, you might well want to write a function that adds two of them together. Julia chooses "the most specific method" to handle the types of arguments. In the case of check_longitude(), we have two specific methods, but we could define a more general method: function check_longitude(loc::Any) println("longitude$loc should be a string or a number")
end
check_longitude (generic function with 3 methods)


This method of check_longitude() is called when the loc argument is neither a Real number or a String. It is the most general method, and won't be called at all if a more specific method is available.

方法定义中的类型参数

julia>function test(a::T) where T <: Real
println("$a is a$T")
end
test (generic function with 1 methods)

julia> test(2.3)
2.3 is a Float64

julia> test(2)
2 is a Int64

julia> test(.02)
0.02 is a Float64

julia> test(pi)
π = 3.1415926535897... is a Irrational{:π}

julia> test(22//7)
22//7 is a Rational{Int64}

julia> test(0xff)
255 is a UInt8


The test() method automatically extracts the type of the single argument a passed to it and stores it in the 'variable' T. For this function, the definition of T was where T is a subtype of Real, so the type of T must be a subtype of the Real type (it can be any real number, but not a complex number). 'T' can be used like any other variable — in this method it's just printed out using string interpolation. (It doesn't have to be T, but it nearly always is!)

This mechanism is useful when you want to constrain the arguments of a particular method definition to be of a particular type. For example, the type of argument a must belong to the Real number supertype, so this test() method doesn't apply when a isn't a number, because then the type of the argument isn't a subtype of Real:

julia> test("str")
ERROR: MethodError: no method matching test(::ASCIIString)

julia> test(1:3)
ERROR: MethodError: no method matching test(::UnitRange{Int64})


Here's an example where you might want to write a method definition that applies to all one-dimensional integer arrays. It finds all the odd numbers in an array:

function findodds(a::Array{T,1}) where T <: Integer
filter(isodd, a)
end
findodds (generic function with 1 method)

julia> findodds(collect(1:20))
10-element Array{Int64,1}:
1
3
5
7
9
11
13
15
17
19


but can't be used for arrays of real numbers:

julia> findodds([1, 2, 3, 4, 5, 6, 7, 8, 9, 10.0])
ERROR: MethodError: no method matching findodds(::Array{Float64,1})
Closest candidates are:
findodds(::Array{T<:Integer,1}) where T<:Integer at REPL[13]:2


Note that, in this simple example, because you're not using the type information inside the method definition, you might be better off sticking to the simpler way of defining methods, by adding type information to the arguments:

function findodds(a::Array{Int64,1})
findall(isodd, a)
end


But if you wanted to do things inside the method that depended on the types of the arguments, then the type parameters approach will be useful.

 « Introducing JuliaFunctions » Controlling the flow Dictionaries and sets

Dictionaries and Sets

 « Introducing JuliaDictionaries and sets » Functions Strings and characters

字典 Dict

创建字典

julia> dict = Dict("a" => 1, "b" => 2, "c" => 3)
Dict{String,Int64} with 3 entries:
"c" => 3
"b" => 2
"a" => 1


dict 现在是字典了。键是“a”、“b”和“c”，对应的值是1、2和3。操作符 => 称为 Pair 函数。在字典中，键总是唯一的 - 不能有两个同名的键。

julia> dict = Dict{String,Integer}("a"=>1, "b" => 2)
Dict{String,Integer} with 2 entries:
"b" => 2
"a" => 1


julia> dict = Dict(string(i) => sind(i) for i = 0:5:360)
Dict{String,Float64} with 73 entries:
"320" => -0.642788
"65"  => 0.906308
"155" => 0.422618
⋮     => ⋮


julia> dict = Dict{String,Int64}()
Dict{String,Int64} with 0 entries


or you can omit the types, and get an untyped dictionary:

julia> dict = Dict()
Dict{Any,Any} with 0 entries


It's sometimes useful to create dictionary entries using a for loop:

files = ["a.txt", "b.txt", "c.txt"]
fvars = Dict()
for (n, f) in enumerate(files)
fvars["x_$(n)"] = f end  This is one way you could create a set of 'variables' stored in a dictionary: julia> fvars Dict{Any,Any} with 3 entries: "x_1" => "a.txt" "x_2" => "b.txt" "x_3" => "c.txt"  查看内容 To get a value, if you have the key: julia> dict = Dict("a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5) julia> dict["a"] 1  if the keys are strings. Or, if the keys are symbols: julia> symdict = Dict(:x => 1, :y => 3, :z => 6) Dict{Symbol,Int64} with 3 entries: :z => 6 :x => 1 :y => 3  julia> symdict[:x] 1  Or if the keys are integers: julia> intdict = Dict(1 => "one", 2 => "two", 3 => "three") Dict{Int64,String} with 3 entries: 2 => "two" 3 => "three" 1 => "one"  julia> intdict[2] "two"  You can instead use the get() function, and provide a fail-safe default value if there's no value for that particular key: julia> dict = Dict("a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5)  julia> get(dict, "a", 0) 1 julia> get(dict, "Z", 0) 0  If you don't use a default value as a safety precaution like this, you'll get an error if there's no key: julia> get(dict, "Z") ERROR: MethodError: no method matching get(::Dict{String,Int64}, ::String) Closest candidates are: get(::Dict{K,V}, ::Any, ::Any) where {K, V} at dict.jl:508 get(::Base.EnvDict, ::AbstractString, ::Any) at env.jl:77  If you don't want get() to provide a default value, use a try...catch block: try dict["Z"] catch error if isa(error, KeyError) println("sorry, I couldn't find anything") end end sorry, I couldn't find anything  To change a value assigned to an existing key (or assign a value to a hitherto unseen key): julia> dict["a"] = 10 10  Keys Keys must be unique for a dictionary. There's always only one key called a in this dictionary, so when you assign a value to a key that already exists, you're not creating a new one, just modifying an existing one. To see if the dictionary contains a key, use haskey(): julia> haskey(dict, "Z") false  To check for the existence of a key/value pair: julia> in(("b" => 2), dict) true  To add a new key and value to a dictionary, use this: julia> dict["d"] = 4 4  You can delete a key from the dictionary, using delete!(): julia> delete!(dict, "d") Dict{String,Int64} with 4 entries: "c" => 3 "e" => 5 "b" => 2 "a" => 1  You'll notice that the dictionary doesn't seem to be sorted in any way — at least, the keys are in no particular order. This is due to the way they're stored, and you can't sort them in place. (But see Sorting, below.) To get all keys, use the keys() function: julia> dict = Dict("a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5); julia> keys(dict) Base.KeySet for a Dict{String,Int64} with 5 entries. Keys: "c" "e" "b" "a" "d"  The result is an iterator that has just one job: to iterate through a dictionary key by key: julia> collect(keys(dict)) 5-element Array{String,1}: "c" "e" "b" "a" "d" julia> [uppercase(key) for key in keys(dict)] 5-element Array{Any,1}: "C" "E" "B" "A" "D"  This uses the list comprehension form ([ new-element for loop-variable in iterator ]) and each new element is collected into an array. An alternative would be: julia> map(uppercase, collect(keys(dict))) 5-element Array{String,1}: "C" "E" "B" "A" "D"  Values To retrieve all the values, use the values() function: julia> values(dict) Base.ValueIterator for a Dict{String,Int64} with 5 entries. Values: 3 5 2 1 4  If you want to go through a dictionary and process each key/value, you can make use the fact that dictionaries themselves are iterable objects: julia> for kv in dict println(kv) end "c"=>3 "e"=>5 "b"=>2 "a"=>1 "d"=>4  where kv is a tuple containing each key/value pair in turn. Or you could do: julia> for k in keys(dict) println(k, " ==> ", dict[k]) end c ==> 3 e ==> 5 b ==> 2 a ==> 1 d ==> 4  Even better, you can use a key/value tuple to simplify the iteration even more: julia> for (key, value) in dict println(key, " ==> ", value) end c ==> 3 e ==> 5 b ==> 2 a ==> 1 d ==> 4  Here's another example: for tuple in Dict("1"=>"Hydrogen", "2"=>"Helium", "3"=>"Lithium") println("Element$(tuple[1]) is $(tuple[2])") end Element 1 is Hydrogen Element 2 is Helium Element 3 is Lithium  (Notice the string interpolation operator, $. This allows you to use a variable's name in a string and get the variable's value when the string is printed. You can include any Julia expression in a string using $().) 字典排序 Because dictionaries don't store the keys in any particular order, you might want to output the dictionary to a sorted array to obtain the items in order: julia> dict = Dict("a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6) Dict{String,Int64} with 6 entries: "f" => 6 "c" => 3 "e" => 5 "b" => 2 "a" => 1 "d" => 4  julia> for key in sort(collect(keys(dict))) println("$key => $(dict[key])") end a => 1 b => 2 c => 3 d => 4 e => 5 f => 6  If you really need to have a dictionary that remains sorted all the time, you can use the SortedDict data type from the DataStructures.jl package (after having installed it). julia> import DataStructures julia> dict = DataStructures.SortedDict("b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6) DataStructures.SortedDict{String,Int64,Base.Order.ForwardOrdering} with 5 entries: "b" => 2 "c" => 3 "d" => 4 "e" => 5 "f" => 6  julia> dict["a"] = 1 1  julia> dict DataStructures.SortedDict{String,Int64,Base.Order.ForwardOrdering} with 6 entries: "a" => 1 "b" => 2 "c" => 3 "d" => 4 "e" => 5 "f" => 6  例子：统计单词 A simple application of a dictionary is to count how many times each word appears in a piece of text. Each word is a key, and the value of the key is the number of times that word appears in the text. Let's count the words in the Sherlock Holmes stories. I've downloaded the text from the excellent Project Gutenberg and stored them in a file "sherlock-holmes-canon.txt". To create a list of words from the loaded text in canon, we'll split the text using a regular expression, and convert every word to lowercase. (There are probably faster methods.) julia> f = open("sherlock-holmes-canon.txt") julia> wordlist = String[] julia> for line in eachline(f) words = split(line, r"\W") map(w -> push!(wordlist, lowercase(w)), words) end julia> filter!(!isempty, wordlist) julia> close(f)  wordlist is now an array of nearly 700,000 words: julia> wordlist[1:20] 20-element Array{String,1}: "THE" "COMPLETE" "SHERLOCK" "HOLMES" "Arthur" "Conan" "Doyle" "Table" "of" "contents" "A" "Study" "In" "Scarlet" "The" "Sign" "of" "the" "Four" "The"  To store the words and the word counts, we'll create a dictionary: julia> wordcounts = Dict{String,Int64}() Dict{String,Int64} with 0 entries  To build the dictionary, loop through the list of words, and use get() to look up the current tally, if any. If the word has already been seen, the count can be increased. If the word hasn't been seen before, the fall-back third argument of get() ensures that the absence doesn't cause an error, and 1 is stored instead. for word in wordlist wordcounts[word]=get(wordcounts, word, 0) + 1 end  Now you can look up words in the wordcounts dictionary and find out how many times they appear: julia> wordcounts["watson"] 1040 julia> wordcounts["holmes"] 3057 julia> wordcounts["sherlock"] 415 julia> wordcounts["lestrade"] 244  Dictionaries aren't sorted, but you can use the collect() and keys() functions on the dictionary to collect the keys and then sort them. In a loop you can work through the dictionary in alphabetical order: for i in sort(collect(keys(wordcounts))) println("$i, $(wordcounts[i])") end 000, 5 1, 8 10, 7 100, 4 1000, 9 104, 1 109, 1 10s, 2 10th, 1 11, 9 1100, 1 117, 2 117th, 2 11th, 1 12, 2 120, 2 126b, 3 ⋮ zamba, 2 zeal, 5 zealand, 3 zealous, 3 zenith, 1 zeppelin, 1 zero, 2 zest, 3 zig, 1 zigzag, 3 zigzagged, 1 zinc, 3 zion, 2 zoo, 1 zoology, 2 zu, 1 zum, 2 â, 41 ã, 4  But how do you find out the most common words? One way is to use collect() to convert the dictionary to an array of tuples, and then to sort the array by looking at the last value of each tuple: julia> sort(collect(wordcounts), by = tuple -> last(tuple), rev=true) 19171-element Array{Pair{String,Int64},1}: ("the",36244) ("and",17593) ("i",17357) ("of",16779) ("to",16041) ("a",15848) ("that",11506) ⋮ ("enrage",1) ("smuggled",1) ("lounges",1) ("devotes",1) ("reverberated",1) ("munitions",1) ("graybeard",1)  To see only the top 20 words: julia> sort(collect(wordcounts), by = tuple -> last(tuple), rev=true)[1:20] 20-element Array{Pair{String,Int64},1}: ("the",36244) ("and",17593) ("i",17357) ("of",16779) ("to",16041) ("a",15848) ("that",11506) ("it",11101) ("in",10766) ("he",10366) ("was",9844) ("you",9688) ("his",7836) ("is",6650) ("had",6057) ("have",5532) ("my",5293) ("with",5256) ("as",4755) ("for",4713)  In a similar way, you can use the filter() function to find, for example, all words that start with "k" and occur less than four times: julia> filter(tuple -> startswith(first(tuple), "k") && last(tuple) < 4, collect(wordcounts)) 73-element Array{Pair{String,Int64},1}: ("keg",1) ("klux",2) ("knifing",1) ("keening",1) ("kansas",3) ⋮ ("kaiser",1) ("kidnap",2) ("keswick",1) ("kings",2) ("kratides",3) ("ken",2) ("kindliness",2) ("klan",2) ("keepsake",1) ("kindled",2) ("kit",2) ("kicking",1) ("kramm",2) ("knob",1)  更加复杂的结构 A dictionary can hold many different types of values. Here for example is a dictionary where the keys are strings and the values are arrays of arrays of points (assuming that the Point type has been defined already). For example, this could be used to store graphical shapes describing the letters of the alphabet (some of which have two or more loops): julia> p = Dict{String, Array{Array}}() Dict{String,Array{Array{T,N},N}} julia> p["a"] = Array[[Point(0,0), Point(1,1)], [Point(34, 23), Point(5,6)]] 2-element Array{Array{T,N},1}: [Point(0.0,0.0), Point(1.0,1.0)] [Point(34.0,23.0), Point(5.0,6.0)] julia> push!(p["a"], [Point(34.0,23.0), Point(5.0,6.0)]) 3-element Array{Array{T,N},1}: [Point(0.0,0.0), Point(1.0,1.0)] [Point(34.0,23.0), Point(5.0,6.0)] [Point(34.0,23.0), Point(5.0,6.0)]  Or create a dictionary with some already-known values: julia> d = Dict("shape1" => Array [ [ Point(0,0), Point(-20,57)], [Point(34, -23), Point(-10,12) ] ]) Dict{String,Array{Array{T,N},1}} with 1 entry: "shape1" => Array [ [ Point(0.0,0.0), Point(-20.0,57.0)], [Point(34.0,-23.0), Point(-10.0,12.0) ] ]  Add another array to the first one: julia> push!(d["shape1"], [Point(-124.0, 37.0), Point(25.0,32.0)]) 3-element Array{Array{T,N},1}: [Point(0.0,0.0), Point(-20.0,57.0)] [Point(34.0,-23.0), Point(-10.0,12.0)] [Point(-124.0,37.0), Point(25.0,32.0)]  集合 Set Set 是元素的集合，就像是一个没有重复元素的字典或数组。 Set 和其他类型的集合有两个不同之处： • Set 中每个元素只能有一份 • 元素的顺序不重要 （而数组对同一个元素可以有多份，并且是有序的） 你可以通过使用 Set 构造函数创建一个空的集合： julia> colors = Set() Set{Any}({})  和 Julia 的其他地方一样，你可以指定类型： julia> primes = Set{Int64}() Set(Int64)[]  可以一次操作创建和填充 Set： julia> colors = Set{String}(["red","green","blue","yellow"]) Set(String["yellow","blue","green","red"])  或者你可以让 Julia “猜出类型”： julia> colors = Set(["red","green","blue","yellow"]) Set{String}({"yellow","blue","green","red"})  相当一部分处理数组的函数也可以用于处理集合。例如，将元素添加到集合 类似于 将元素添加到数组。您可以使用 push!() julia> push!(colors, "black") Set{String}({"yellow","blue","green","black","red"})  But you can't use pushfirst!(), because that works only for things that have a concept of "first", like arrays. What happens if you try to add something to the set that's already there? Absolutely nothing. You don't get a copy added, because it's a set, not an array, and sets don't store repeated elements. To see if something is in the set, you can use in(): julia> in("green", colors) true  There are some standard operations you can do with sets, namely find their union, intersection, and difference, with the functions, union(), intersect(), and setdiff(): julia> rainbow = Set(["red","orange","yellow","green","blue","indigo","violet"]) Set(String["indigo","yellow","orange","blue","violet","green","red"])  The union of two sets is the set of everything that is in one or the other sets. The result is another set – so you can't have two "yellow"s here, even though we've got a "yellow" in each set: julia> union(colors, rainbow) Set(String["indigo","yellow","orange","blue","violet","green","black","red"])  The intersection of two sets is the set that contains every element that belongs to both sets: julia> intersect(colors, rainbow) Set(String["yellow","blue","green","red"])  The difference between two sets is the set of elements that are in the first set, but not in the second. This time, the order in which you supply the sets matters. The setdiff() function finds the elements that are in the first set, colors, but not in the second set, rainbow: julia> setdiff(colors, rainbow) Set(String["black"])  其他函数 处理数组和集合的函数有时也适用于字典和其他集合。例如，某些 集合的操作可应用于词典，而不仅仅是 Set 和 数组： julia> d1 = Dict(1=>"a", 2 => "b") Dict{Int64,String} with 2 entries: 2 => "b" 1 => "a" julia> d2 = Dict(2 => "b", 3 =>"c", 4 => "d") Dict{Int64,String} with 3 entries: 4 => "d" 2 => "b" 3 => "c" julia> union(d1, d2) 4-element Array{Pair{Int64,String},1}: 2=>"b" 1=>"a" 4=>"d" 3=>"c" julia> intersect(d1, d2) 1-element Array{Pair{Int64,String},1}: 2=>"b" julia> setdiff(d1, d2) 1-element Array{Pair{Int64,String},1}: 1=>"a"  请注意，结果是以对数组的形式返回的，而不是以字典的形式返回的。 filter(), map()collect() 等函数 (我们已经看到它们用于数组) 也适用于字典： julia> filter((k, v) -> k == 1, d1) Dict{Int64,String} with 1 entry: 1 => "a"  有一个 merge() 函数，它可以合并两个字典： julia> merge(d1, d2) Dict{Int64,String} with 4 entries: 4 => "d" 2 => "b" 3 => "c" 1 => "a"  findmin()函数可以在字典中找到最小值，然后返回值及其键。 julia> d1 = Dict(:a => 1, :b => 2, :c => 0) Dict{Symbol,Int64} with 3 entries: :a => 1 :b => 2 :c => 0 julia> findmin(d1) (0, :c)   « Introducing JuliaDictionaries and sets » Functions Strings and characters Strings and Characters  « Introducing JuliaStrings and characters » Dictionaries and sets Working with text files Strings and characters Strings 字符串是由一个或多个字符组成的序列，通常用双引号括起来： "this is a string"  关于字符串，有两件重要的事情需要了解。 一个是，它们是不可变的。一旦它们被创建，你就不能改变它们。但是，从现有字符串的某些部分创建新字符串是很容易的。 第二点是，在使用两个特定字符时必须小心：双引号(")和美元符号($)。如果要在字符串中包含双引号字符，则必须在其前面加上反斜杠，否则字符串的其余部分将被解释为 Julia 代码，可能会产生有趣的结果。如果您想在字符串中包含一个美元符号($)，也应该以反斜杠开头，因为它用于字符串插值 string interpolation julia> demand = "You owe me \$50!"
"You owe me \$50!" julia> println(demand) You owe me$50!

julia> demandquote = "He said, \"You owe me \$50!\"" "He said, \"You owe me \$50!\""


julia> """this is "a" string"""
"this is \"a\" string"


• r" " 表示这是一个正则表达式
• v" " 表示这是一个版本字符串
• b" " 表示这是一个字节字面量
• raw" " 表示这是一个 raw string 不允许插值

String interpolation

"The value of x is n."


nx 的当前值。任何 Julia 的表达式都可以插入到具有 $() 结构的字符串中： julia> x = 42 42 julia> "The value of x is$(x)."
"The value of x is 42."


julia> "The value of x is $x." "The value of x is 42."  若要将 Julia 表达式的结果包含在字符串中，请先将表达式括在括号中，然后在其前面加上一个美元符号： julia> "The value of 2 + 2 is$(2 + 2)."
"The value of 2 + 2 is 4."


子字符串

julia> s = String("a load of characters")
"a load of characters"

julia> s[1:end]
"a load of characters"

julia> s[3:6]

julia> s[3:end-6]


for char in s
print(char, "_")
end
a_ _l_o_a_d_ _o_f_ _c_h_a_r_a_c_t_e_r_s_


julia> s[1:1]
"a"

julia> s[1]
'a'


Unicode 字符串

julia> su = String("AéB𐅍CD")
"AéB𐅍CD"

julia> su[1]
'A'

julia> su[2]
'é'

julia> su[3]
ERROR: UnicodeError: invalid character index
in slow_utf8_next(::Array{UInt8,1}, ::UInt8, ::Int64) at ./strings/string.jl:67
in next at ./strings/string.jl:92 [inlined]
in getindex(::String, ::Int64) at ./strings/basic.jl:70


julia> length(su)
6

julia> lastindex(su)
10


isascii() 函数用于测试字符串是 ASCII 还是包含 Unicode 字符：

julia> isascii(su)
false


for i in eachindex(su)
println(thisind(su, i), " -> ", su[i])
end

1 -> A
2 -> é
4 -> B
5 -> 𐅍
9 -> C
10 -> D

“第三个”字符 B 从字符串中的第四个元素开始。

for charindex in eachindex(su)
@show su[charindex]
end
su[charindex] = 'A'
su[charindex] = 'é'
su[charindex] = 'B'
su[charindex] = '𐅍'
su[charindex] = 'C'
su[charindex] = 'D'


对字符串 split 和 join

julia> "s" * "t"
"st"


julia> "s" + "t"
LoadError: MethodError: + has no method matching +(::String, ::String)


- 因此用 *.

julia> "s" ^ 18
"ssssssssssssssssss"


julia> string("s", "t")
"st"


julia> s = "You know my methods, Watson."
"You know my methods, Watson."


split() 函数的简单调用在空格处分割字符串，返回一个分为五段的数组：

julia> split(s)
5-element Array{SubString{String},1}:
"You"
"know"
"my"
"methods,"
"Watson."


julia> split(s, "e")
2-element Array{SubString{String},1}:
"You know my m"
"thods, Watson."

julia> split(s, " m")'
3-element Array{SubString{String},1}:
"You know"
"y"
"ethods, Watson."


julia> split(s, "hod")
2-element Array{SubString{String},1}:
"You know my met"
"s, Watson."


julia> split(s,"")
28-element Array{SubString{String},1}:
"Y"
"o"
"u"
" "
"k"
"n"
"o"
"w"
" "
"m"
"y"
" "
"m"
"e"
"t"
"h"
"o"
"d"
"s"
","
" "
"W"
"a"
"t"
"s"
"o"
"n"
"."


julia> split(s, r"a|e|i|o|u")
8-element Array{SubString{String},1}:
"Y"
""
" kn"
"w my m"
"th"
"ds, W"
"ts"
"n."


julia> split(s, r"a|e|i|o|u", false)
7-element Array{SubString{String},1}:
"Y"
" kn"
"w my m"
"th"
"ds, W"
"ts"
"n."


julia> join(split(s, r"a|e|i|o|u", false), "aiou")
"Yaiou knaiouw my maiouthaiouds, Waioutsaioun."


Splitting using a function

Julia中的许多函数允许您使用函数作为函数调用的一部分。匿名函数很有用，因为您可以进行具有内置智能选择的函数调用。例如，split() 允许您提供一个函数来代替分隔符字符。在下一个示例中，分隔符(奇怪地)指定为ASCII代码为8的倍数的任何大写字符：

julia> split(join(Char.(65:90)),  c -> Int(c) % 8 == 0)
4-element Array{SubString{String},1}:
"ABCDEFG"
"IJKLMNO"
"QRSTUVW"
"YZ"


字符对象

julia> s[1:1]
"a"


julia> s[1]
'a'


julia> string('s') * string('d')
"sd"


julia> string('s', 'd')
"sd"


julia> ('\U1014d', '\u2640', '\u26')
('𐅍','♀','&')


julia> "\U0001014d2\U000026402\u26402\U000000a52\u00a52\U000000352\u00352\x352"
"𐅍2♀2♀2¥2¥2525252"


数字和字符串之间的转换

julia> string(11, base=2)
"1011"

julia> string(11, base=8)
"13"

julia> string(11, base=16)
"b"

julia> string(11)
"11"

julia> a = BigInt(2)^200
1606938044258990275541962092341162602522202993782792835301376

julia> string(a)
"1606938044258990275541962092341162602522202993782792835301376"

julia> string(a, base=16)
"1000000000000000000000000000000000000000000000000"


julia> parse(Int, "100")
100

julia> parse(Int, "100", base=2)
4

julia> parse(Int, "100", base=16)
256

julia> parse(Float64, "100.32")
100.32

julia> parse(Complex{Float64}, "0 + 1im")
0.0 + 1.0im


字符与整数互相转换

Int() 将字符转换为整数，Char()将整数转换为字符。

julia> Char(8253)
'‽': Unicode U+203d (category Po: Punctuation, other)

julia> Char(0x203d) # the Interrobang is Unicode U+203d in hexadecimal
'‽': Unicode U+203d (category Po: Punctuation, other)

julia> Int('‽')
8253

julia> string(Int('‽'), base=16)
"203d"


julia> Int("S"[1])
83


printf 格式

julia> using Printf

julia> @printf("pi = %0.20f", float(pi))
pi = 3.14159265358979311600


julia> @sprintf("pi = %0.20f", float(pi))
"pi = 3.14159265358979311600"


将字符串转换为数组

data="1 2 3 4
5 6 7 8
9 0 1 2"

"1 2 3 4\n5 6 7 8\n9 0 1 2"


julia> using DelimitedFiles
3x4 Array{Float64,2}:
1.0 2.0 3.0 4.0
5.0 6.0 7.0 8.0
9.0 0.0 1.0 2.0


julia> readdlm(IOBuffer(data), Int)
3x4 Array{Int64,2}:
1 2 3 4
5 6 7 8
9 0 1 2


julia> s = "/Users/me/Music/iTunes/iTunes Media/Mobile Applications";


julia> collect(s)
55-element Array{Char,1}:
'/'
'U'
's'
'e'
'r'
's'
'/'
...


julia> split(s, "")
55-element Array{Char,1}:
'/'
'U'
's'
'e'
'r'
's'
'/'
...


julia> count(c -> c == '/', collect(s))
6


julia> count(c -> c == '/', s)
6


查找和替换字符串中的内容

julia> s = "Elementary, my dear Watson";
julia> in('m', s)
true


julia> occursin("Wat", s)
true

julia> occursin("m", s)
true

julia> occursin("mi", s)
false

julia> occursin("me", s)
true


julia> s ="You know my methods, Watson.";

julia> findfirst("meth", s)
13:16

julia> findfirst(r"[aeiou]", s)  # first vowel
2

julia> findfirst(isequal('a'), s) # first occurrence of character 'a'
23


替换

replace()函数的作用是：返回一个新字符串，其中包含一个替换为其他字符的子字符串：

julia> replace("Sherlock Holmes", "e" => "ee")
"Sheerlock Holmees"


You use the => operator to specify the pattern you're looking for, and its replacement. Usually the third argument is another string, as here. But you can also supply a function that processes the result:

julia> replace("Sherlock Holmes", "e" => uppercase)
"ShErlock HolmEs"


where the function (here, the built-in uppercase() function) is applied to the matching substring.

There's no replace! function, where the "!" indicates a function that changes its argument. That's because you can't change a string — they're immutable.

Replacing using functions

Many functions in Julia allow you to supply functions as part of the function call, and you can make good use of anonymous functions for this. Here, for example, is how to use a function to provide random replacements in a replace() function.

julia>  t = "You can never foretell what any one man will do, but you can say with precision what an average number will be up to. Individuals vary, but percentages remain constant.";

julia> replace(t, r"a|e|i|o|u" => (c) -> rand(Bool) ? "0" : "1")
"Y00 c1n n0v0r f1r0t1ll wh1t 0ny 0n0 m0n w1ll d0, b0t y01 c1n s1y w0th pr1c1s10n wh0t 1n 1v0r0g0 n1mb0r w0ll b0 0p t1. Ind1v0d11ls v0ry, b0t p1rc0nt0g0s r0m01n c1nst0nt."

julia> replace(t, r"a|e|i|o|u" => (c) -> rand(Bool) ? "0" : "1")
"Y11 c0n...n1v0r f0r1t0ll wh1t 1ny 0n1 m0n w1ll d1, b1t y10 c1n s1y w1th pr0c1s01n wh0t 0n 0v1r0g0 n1mb1r w0ll b0 1p t1. Ind1v0d01ls v0ry, b1t p0rc1nt1g0s r0m01n c1nst0nt."


正则表达式

You can use regular expressions to find matches for substrings. Some functions that accept a regular expression are:

• replace() changes occurrences of regular expressions
• match() returns the first match or nothing
• eachmatch() returns an iterator that lets you search through all matches
• split() splits a string at every match

Use replace() to replace each consonant with an underscore:

julia> replace("Elementary, my dear Watson!", r"[^aeiou]" => "_")
"__e_e__a________ea___a__o__"


and the following code replaces each vowel with the results of running a function on each match:

julia> replace("Elementary, my dear Watson!", r"[aeiou]" => uppercase)
"ElEmEntAry, my dEAr WAtsOn!"


With replace() you can access the matches if you provide a special substitution string s"", where \1 refers to the first match, \2 to the second, and so on. With this regex operation, each lowercase letter preceded by a space is repeated three times:

julia> replace("Elementary, my dear Watson!", r"(\s)([a-z])" => s"\1\2\2\2")
"Elementary, mmmy dddear Watson!"


For more regular expression fun, there are the -match- functions.

Here I've loaded the complete text of "The Adventures of Sherlock Holmes" from a file into the string called text:

julia> f = "/tmp/adventures-of-sherlock-holmes.txt"
julia> text = read(f, String);


To use the possibility of a match as a Boolean condition, suitable for use in an if statement for example, use occursin().

julia> occursin(r"Opium", text)
false


That's odd. We were expecting to find evidence of the great detective's peculiar pharmacological recreations. In fact, the word "opium" does appear in the text, but only in lower-case, hence this  false result—regular expressions are case-sensitive.

julia> occursin(r"(?i)Opium", text)
true


This is a case-insensitive search, set by the flag (?i)), and it returns true.

You could check every line for the word using a simple loop:

for l in split(text, "\n")
occursin(r"opium", l) && println(l)
end

opium. The habit grew upon him, as I understand, from some
he had, when the fit was on him, made use of an opium den in the
brown opium smoke, and terraced with wooden berths, like the
wrinkled, bent with age, an opium pipe dangling down from between
very short time a decrepit figure had emerged from the opium den,
opium-smoking to cocaine injections, and all the other little
steps - for the house was none other than the opium den in which
lives upon the second floor of the opium den, and who was
learn to have been the lodger at the opium den, and to have been
doing in the opium den, what happened to him when there, where is
"Had he ever showed any signs of having taken opium?"
room above the opium den when I looked out of my window and saw,

For more useable output (in the REPL), add enumerate() and some highlighting:

red = Base.text_colors[:red]; default = Base.text_colors[:default];
for (n, l) in enumerate(split(text, "\n"))
occursin(r"opium", l) && println("$n$(replace(l, "opium" => "$(red)opium$(default)"))")
end

5087 opium. The habit grew upon him, as I understand, from some
5140 he had, when the fit was on him, made use of an opium den in the
5173 brown opium smoke, and terraced with wooden berths, like the
5237 wrinkled, bent with age, an opium pipe dangling down from between
5273 very short time a decrepit figure had emerged from the opium den,
5280 opium-smoking to cocaine injections, and all the other little
5429 steps - for the house was none other than the opium den in which
5486 lives upon the second floor of the opium den, and who was
5510 learn to have been the lodger at the opium den, and to have been
5593 doing in the opium den, what happened to him when there, where is
5846 "Had he ever showed any signs of having taken opium?"
6129 room above the opium den when I looked out of my window and saw,


There's an alternative syntax for adding regex modifiers, such as case-insensitive matches. Notice the "i" immediately following the regex string in the second example:

julia> occursin(r"Opium", text)
false

julia> occursin(r"Opium"i, text)
true


With the eachmatch() function, you apply the regex to the string to produce an iterator. For example, to look for substrings in our text matching the letters "L", followed by some other characters, ending with "ed":

julia> lmatch = eachmatch(r"L.*?ed", text)


The result in lmatch is an iterable object containing all the matches, as RegexMatch objects:

julia> collect(lmatch)[1:10]
10-element Array{RegexMatch,1}:
RegexMatch("London, and proceed")
RegexMatch("London is a pleasant thing indeed")
RegexMatch("Looking for lodgings,\" I answered")
RegexMatch("Lied")
RegexMatch("Life,\" and it attempted")
RegexMatch("Lauriston Gardens wore an ill-omened")
RegexMatch("Let\" card had developed")


We can step through the iterator and look at each match in turn. You can access a number of fields of a RegexMatch, to extract information about the match. These include captures, match, offset, offsets, and regex. For example, the match field contains the matched substring:

for i in lmatch
println(i.match)
end

London - quite so! Your Majesty, as I understand, became entangled
Lodge. As it pulled
Lord, Mr. Wilson, that I was a red
League of the Red
League was founded
London when he was young, and he wanted
LSON" in white letters, upon a corner house, announced
League, and the copying of the 'Encyclopaed
Leadenhall Street Post Office, to be left till called
Let the whole incident be a sealed
Lestrade, being rather puzzled
Lestrade would have noted
...
Lord St. Simon has not already arrived
Lord St. Simon sank into a chair and passed
Lord St. Simon had by no means relaxed
Lordship. "I may be forced
London. What could have happened
London, and I had placed

Other fields include captures, the captured substrings as an array of strings, offset, the offset into the string at which the whole match begins, and offsets, the offsets of the captured substrings.

To get an array of matching strings, use something like this:

julia> collect(m.match for m in eachmatch(r"L.*?ed", text))
58-element Array{SubString{String},1}:
"London - quite so! Your Majesty, as I understand, became entangled"
"Lodge. As it pulled"
"Lord, Mr. Wilson, that I was a red"
"League of the Red"
"League was founded"
"London when he was young, and he wanted"
"Leadenhall Street Post Office, to be left till called"
"Let the whole incident be a sealed"
"Lestrade, being rather puzzled"
"Lestrade would have noted"
...
"Lord St. Simon shrugged"
"Lady St. Simon was decoyed"
"Lord St. Simon has not already arrived"
"Lord St. Simon sank into a chair and passed"
"Lord St. Simon had by no means relaxed"
"Lordship. \"I may be forced"
"London. What could have happened"
"London, and I had placed"


The basic match() function looks for the first match for your regex. Use the match field to extract the information from the RegexMatch object:

julia> match(r"She.*",text).match
"Sherlock Holmes she is always THE woman. I have seldom heard\r"


A more streamlined way of obtaining matching lines from a file is this:

julia> f = "adventures of sherlock holmes.txt"

julia> filter(s -> occursin(r"(?i)Opium", s), map(chomp, readlines(open(f))))
12-element Array{SubString{String},1}:
"opium. The habit grew upon him, as I understand, from some"
"he had, when the fit was on him, made use of an opium den in the"
"brown opium smoke, and terraced with wooden berths, like the"
"wrinkled, bent with age, an opium pipe dangling down from between"
"very short time a decrepit figure had emerged from the opium den,"
"opium-smoking to cocaine injections, and all the other little"
"steps - for the house was none other than the opium den in which"
"lives upon the second floor of the opium den, and who was"
"learn to have been the lodger at the opium den, and to have been"
"doing in the opium den, what happened to him when there, where is"
"\"Had he ever showed any signs of having taken opium?\""
"room above the opium den when I looked out of my window and saw,"


Making a Regex

Sometimes you want to make a regular expression from within your code. You can do this by making a Regex object. Here is one way you could count the number of vowels in the text:

f = open("sherlock-holmes.txt")

text = read(f, String)

for vowel in "aeiou"
r = Regex(string(vowel))
l = [m.match for m = eachmatch(r, thetext)]
println("there are $(length(l)) letter \"$vowel\"s in the text.")
end

there are 219626 letter "a"s in the text.
there are 337212 letter "e"s in the text.
there are 167552 letter "i"s in the text.
there are 212834 letter "o"s in the text.
there are 82924 letter "u"s in the text.

判断和更改字符串

There are lots of functions for testing and changing strings:

• length(str) length of string
• sizeof(str) length/size
• startswith(strA, strB) does strA start with strB?
• endswith(strA, strB) does strA end with strB?
• occursin(strA, strB) does strA occur in strB?
• all(isletter, str) is str entirely letters?
• all(isnumeric, str) is str entirely number characters?
• isascii(str) is str ASCII?
• all(iscntrl, str) is str entirely control characters?
• all(isdigit, str) is str 0-9?
• all(ispunct, str) does str consist of punctuation?
• all(isspace, str) is str whitespace characters?
• all(isuppercase, str) is str uppercase?
• all(islowercase, str) is str entirely lowercase?
• all(isxdigit, str) is str entirely hexadecimal digits?
• uppercase(str) return a copy of str converted to uppercase
• lowercase(str) return a copy of str converted to lowercase
• titlecase(str) return copy of str with the first character of each word converted to uppercase
• uppercasefirst(str) return copy of str with first character converted to uppercase
• lowercasefirst(str) return copy of str with first character converted to lowercase
• chop(str) return a copy with the last character removed
• chomp(str) return a copy with the last character removed only if it's a newline

Streams

To write to a string, you can use a Julia stream. The sprint() (String Print) function lets you use a function as the first argument, and uses the function and the rest of the arguments to send information to a stream, returning the result as a string.

For example, consider the following function, f. The body of the function maps an anonymous 'print' function over the arguments, enclosing them with angle brackets. When used by sprint, the function f processes the remaining arguments and sends them to the stream.

function f(io::IO, args...)
map((a) -> print(io,"<",a, ">"), args)
end
f (generic function with 1 method)

julia> sprint(f, "fred", "jim", "bill", "fred blogs")
"<fred><jim><bill><fred blogs>"


Functions like println() can take an IOBuffer or stream as their first argument. This lets you print to streams instead of printing to the standard output device:

julia> iobuffer = IOBuffer()
IOBuffer(data=Uint8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)

julia> for i in 1:100
println(iobuffer, string(i))
end


After this, the in-memory stream called iobuffer is full of numbers and newlines, even though nothing was printed on the terminal. To copy the contents of iobuffer from the stream to a string or array, you can use take!():

julia> String(take!(iobuffer))
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14 ... \n98\n99\n100\n"

 « Introducing JuliaStrings and characters » Dictionaries and sets Working with text files

Working with Text Files

 « Introducing JuliaWorking with text files » Strings and characters Working with dates and times

从文件中读取

打开

f = open("sherlock-holmes.txt")


f 现在是 Julia 与磁盘上的文件的连接。完成文件处理后，应使用以下命令关闭连接：

close(f)


open("sherlock-holmes.txt") do file
# do stuff with the open file
end


totaltime, totallines = open("sherlock-holmes.txt") do f
linecounter = 0
timetaken = @elapsed for l in eachline(f)
linecounter += 1
end
(timetaken, linecounter)
end

julia> totaltime, totallines
(0.004484679, 76803)


一次性读取整个文件 !

julia> s = read(f, String)


s = open("sherlock-holmes.txt") do file
end


julia> f = open("sherlock-holmes.txt");

julia> lines = readlines(f)
76803-element Array{String,1}:
"THE ADVENTURES OF SHERLOCK HOLMES by SIR ARTHUR CONAN DOYLE\r\n"
"\r\n"
"   I. A Scandal in Bohemia\r\n"
"  II. The Red-headed League\r\n"
...
"Holmes, rather to my disappointment, manifested no further\r\n"
"interest in her when once she had ceased to be the centre of one\r\n"
"of his problems, and she is now the head of a private school at\r\n"
"Walsall, where I believe that she has met with considerable success.\r\n"
julia> close(f)


counter = 1
for l in lines
println("$counter$l")
counter += 1
end

1 THE ADVENTURES OF SHERLOCK HOLMES by SIR ARTHUR CONAN DOYLE
2
3    I. A Scandal in Bohemia
4   II. The Red-headed League
5  III. A Case of Identity
6   IV. The Boscombe Valley Mystery
...
12638 interest in her when once she had ceased to be the centre of one
12639 of his problems, and she is now the head of a private school at
12640 Walsall, where I believe that she has met with considerable success.


一行行读取

The eachline() function turns a source into an iterator. This allows you to process a file a line at a time:

open("sherlock-holmes.txt") do file
for ln in eachline(file)
println("$(length(ln)),$(ln)")
end
end

1, THE ADVENTURES OF SHERLOCK HOLMES by SIR ARTHUR CONAN DOYLE
2,
28,    I. A Scandal in Bohemia
29,   II. The Red-headed League
26,  III. A Case of Identity
35,   IV. The Boscombe Valley Mystery
…
62, the island of Mauritius. As to Miss Violet Hunter, my friend
60, Holmes, rather to my disappointment, manifested no further
66, interest in her when once she had ceased to be the centre of one
65, of his problems, and she is now the head of a private school at
70, Walsall, where I believe that she has met with considerable success.

 open("sherlock-holmes.txt") do f
line = 1
while !eof(f)
println("$line$x")
line += 1
end
end


open("sherlock-holmes.txt") do f
for i in enumerate(eachline(f))
println(i[1], ": ", i[2])
end
end


function shout(f::IOStream)
end

julia> shoutversion = open(shout, "sherlock-holmes.txt");

julia> shoutversion[30237:30400]
"ELEMENTARY PROBLEMS. LET HIM, ON MEETING A\nFELLOW-MORTAL, LEARN AT A GLANCE TO DISTINGUISH THE HISTORY OF THE\nMAN, AND THE TRADE OR  PROFESSION TO WHICH HE BELONGS. "


You can use the DelimitedFiles.readdlm() function to read lines delimited with certain characters, such as data files, arrays stored as text files, and tables. If you use the DataFrames package, there's also a readtable() specifically designed to read data into a table. See also the package CSV.jl.

对路径和文件名进行操作

These functions will be useful for working with filenames:

• cd(path) changes the current directory
• readdir(path) returns a lists of the contents of a named directory, or the current directory,
• abspath(path) adds the current directory's path to a filename to make an absolute pathname
• joinpath(str, str, ...) assembles a pathname from pieces
• isdir(path) tells you whether the path is a directory
• splitdir(path) - split a path into a tuple of the directory name and file name.
• splitdrive(path) - on Windows, split a path into the drive letter part and the path part. On Unix systems, the first component is always the empty string.
• splitext(path) - if the last component of a path contains a dot, split the path into everything before the dot and everything including and after the dot. Otherwise, return a tuple of the argument unmodified and the empty string.
• expanduser(path) - replace a tilde character at the start of a path with the current user's home directory.
• normpath(path) - normalize a path, removing "." and ".." entries.
• realpath(path) - canonicalize a path by expanding symbolic links and removing "." and ".." entries.
• homedir() - current user's home directory.
• dirname(path) - get the directory part of a path.
• basename(path)- get the file name part of a path.

To work on a restricted selection of files in a directory, use filter() and an anonymous function to filter the file names and just keep the ones you want. (filter() is more of a fishing net or sieve, rather than a coffee filter, in that it catches what you want to keep.)

for f in filter(x -> endswith(x, "jl"), readdir())
println(f)
end

Astro.jl
calendar.jl
constants.jl
coordinates.jl
...
pseudoscience.jl
riseset.jl
sidereal.jl
sun.jl
utils.jl
vsop87d.jl


If you want to match a group of files using a regular expression, then use occursin(). Let's look for files with ".jpg" or ".png" suffixes (remembering to escape the "."):

for f in filter(x -> occursin(r"(?i)\.jpg|\.png", x), readdir())
println(f)
end

034571172750.jpg
034571172750.png
51ZN2sCNfVL._SS400_.jpg
51bU7lucOJL._SL500_AA300_.jpg
Voronoy.jpg
kblue.png
korange.png
penrose.jpg
r-home-id-r4.png
wave.jpg

To examine a file hierarchy, use walkdir(), which lets you work through a directory, and examine the files in each directory in turn.

文件信息

If you want information about a specific file, use stat("pathname"), and then use one of the fields to find out the information. Here's how to get all the information and the field names listed for a file "i":

 for n in fieldnames(typeof(stat("i")))
println(n, ": ", getfield(stat("i"),n))
end

device: 16777219
inode: 2955324
mode: 16877
uid: 502
gid: 20
rdev: 0
size: 32062
blksize: 4096
blocks: 0
mtime:1.409769933e9
ctime:1.409769933e9

You can access these fields via a 'stat' structure:

julia> s = stat("Untitled1.ipynb")
StatStruct(mode=100644, size=64424)

julia> s.ctime
1.446649269e9


and you can also use some of them directly:

julia> ctime("Untitled2.ipynb")
1.446649269e9


although not size:

julia> s.size
64424


To work on specific files that meet conditions — all IPython files modified after a certain date, for example — you could use something like this:

using Dates
function output_file(path)
println(stat(path).size, ": ", path)
end

for afile in filter!(f -> endswith(f, "ipynb") && (mtime(f) > Dates.datetime2unix(DateTime("2015-11-03T09:00"))),
output_file(realpath(afile))
end


与文件系统交互

The cp(), mv(), rm(), and touch() functions have the same names and functions as their Unix shell counterparts.

To convert filenames to pathnames, use abspath(). You can map this over a list of files in a directory:

julia> map(abspath, readdir())
67-element Array{String,1}:
"/Users/me/.CFUserTextEncoding"
"/Users/me/.DS_Store"
"/Users/me/.Trash"
"/Users/me/.Xauthority"
"/Users/me/.ahbbighrc"
"/Users/me/.apdisk"
"/Users/me/.atom"
...


To restrict the list to filenames that contain a particular substring, use an anonymous function inside filter() — something like this:

julia> filter(x -> occursin("re", x), map(abspath, readdir()))
4-element Array{String,1}:
"/Users/me/.DS_Store"
"/Users/me/.gitignore"
"/Users/me/.hgignore_global"
"/Users/me/Pictures"
...



To restrict the list to regular expression matches, try this:

julia> filter(x -> occursin(r"recur.*\.jl", x), map(abspath, readdir()))
2-element Array{String,1}:
"/Users/me/julia/recursive-directory-scan.jl"
"/Users/me/julia/recursive-text.jl"


写入文件

To write to a text file, open it using the "w" flag and make sure that you have permission to create the file in the specified directory:

open("/tmp/t.txt", "w") do f
write(f, "A, B, C, D\n")
end


Here's how to write 20 lines of 4 random numbers between 1 and 10, separated by commas:

function fourrandom()
return rand(1:10,4)
end

open("/tmp/t.txt", "w") do f
for i in 1:20
n1, n2, n3, n4 = fourrandom()
write(f, "$n1,$n2, $n3,$n4 \n")
end
end


A quicker alternative to this is to use the DelimitedFiles.writedlm() function, described next:

using DelimitedFiles
writedlm("/tmp/test.txt", rand(1:10, 20, 4), ", ")


在文件中写入和读取数组

In the DelimitedFiles package are two convenient functions, writedlm() and readdlm(). These let you read/write an array or collection from/to a file.

writedlm() writes the contents of an object to a text file, and readdlm() reads the data from a file into an array:

julia> numbers = rand(5,5)
5x5 Array{Float64,2}:
0.913583  0.312291  0.0855798  0.0592331  0.371789
0.13747   0.422435  0.295057   0.736044   0.763928
0.360894  0.434373  0.870768   0.469624   0.268495
0.620462  0.456771  0.258094   0.646355   0.275826
0.497492  0.854383  0.171938   0.870345   0.783558

julia> writedlm("/tmp/test.txt", numbers)


You can see the file using the shell (type a semicolon ";" to switch):

<shell> cat "/tmp/test.txt"
.9135833328830523	.3122905420350348	.08557977218948465	.0592330821115965	.3717889559226475
.13747015238054083	.42243494637594203	.29505701073304524	.7360443978397753	.7639280496847236
.36089432672073607	.43437288984307787	.870767989032692	.4696243851552686	.26849468736154325
.6204624598015906	.4567706404666232	.25809436255988105	.6463554854347682	.27582613759302377
.4974916625466639	.8543829989347014	.17193814498701587	.8703447748713236	.783557793485824


The elements are separated by tabs unless you specify another delimiter. Here, a colon is used to delimit the numbers:

julia> writedlm("/tmp/test.txt", rand(1:6, 10, 10), ":")

shell> cat "/tmp/test.txt"
3:3:3:2:3:2:6:2:3:5
3:1:2:1:5:6:6:1:3:6
5:2:3:1:4:4:4:3:4:1
3:2:1:3:3:1:1:1:5:6
4:2:4:4:4:2:3:5:1:6
6:6:4:1:6:6:3:4:5:4
2:1:3:1:4:1:5:4:6:6
4:4:6:4:6:6:1:4:2:3
1:4:4:1:1:1:5:6:5:6
2:4:4:3:6:6:1:1:5:5


To read in data from a text file, you can use readdlm().

julia> numbers = rand(5,5)
5x5 Array{Float64,2}:
0.862955  0.00827944  0.811526  0.854526  0.747977
0.661742  0.535057    0.186404  0.592903  0.758013
0.800939  0.949748    0.86552   0.113001  0.0849006
0.691113  0.0184901   0.170052  0.421047  0.374274
0.536154  0.48647     0.926233  0.683502  0.116988

julia> writedlm("/tmp/test.txt", numbers)

julia> numbers = readdlm("/tmp/test.txt")
5x5 Array{Float64,2}:
0.862955  0.00827944  0.811526  0.854526  0.747977
0.661742  0.535057    0.186404  0.592903  0.758013
0.800939  0.949748    0.86552   0.113001  0.0849006
0.691113  0.0184901   0.170052  0.421047  0.374274
0.536154  0.48647     0.926233  0.683502  0.116988


There are also a number of Julia packages specifically designed for reading and writing data to files, including DataFrames.jl and CSV.jl. Look through the Julia package directory for these and more.

 « Introducing JuliaWorking with text files » Strings and characters Working with dates and times

Working with Dates and Times

 « Introducing JuliaWorking with dates and times » Working with text files Plotting

Working with dates and times

Functions for working with dates and times are provided in the standard package Dates. To use any of the time and date functions, you must do one of the following:

• using Dates
• import Dates

If you use import Dates functions, you’ll need to prefix every function with an explicit Dates., e.g. Dates.dayofweek(dt), as shown in this chapter. However, if you add the line using Dates to your code, this brings all exported Dates functions into Main, and they can be used without the Dates. prefix.

类型

This diagram shows the relationship between the various types used to store Times, Dates, and DateTimes.

Date, Time 以及 DateTimes

There are three main datatypes available:

• A Dates.Time object represents a precise moment of time in a day. It doesn't say anything about the day of the week, or the year, though. It's accurate to a nanosecond.
• A Dates.Date object represents just a date: no time zones, no daylight saving issues, etc... It's accurate to, well, a day.
• A Dates.DateTime object is a combination of a date and a time of day, and so it specifies an exact moment in time. It's accurate to a millisecond or so.

Use one of these constructors to make the type of object you want:

julia> rightnow = Dates.Time(Dates.now()) # a Dates.Time object
16:51:56.374

julia> birthday = Dates.Date(1997,3,15)   # a Dates.Date object
1997-03-15

julia> armistice = Dates.DateTime(1918,11,11,11,11,11) # a Dates.DateTime object
1918-11-11T11:11:11


The Dates.today() function returns a Date object for the current date:

julia> datetoday = Dates.today()
2014-09-02


The Dates.now() function returns a DateTime object for the current instant in time:

julia> datetimenow = Dates.now()
2014-09-02T08:20:07.437


(We used Dates.now() earlier to define rightnow, then converted it to a Dates.Time using Dates.Time().)

Sometimes you want UTC (the reference time for the world, without local adjustments for daylight savings):

julia> Dates.now(Dates.UTC)
2014-09-02T08:27:54.14


To create an object from a formatted string, use the DateTime() function in Dates, and supply a suitable format string that matches the formatting:

julia> Dates.DateTime("20140529 120000", "yyyymmdd HHMMSS")
2014-05-29T12:00:00

julia> Dates.DateTime("18/05/2009 16:12", "dd/mm/yyyy HH:MM")
2009-05-18T16:12:00

julia> vacation = Dates.DateTime("2014-09-02T08:20:07") # defaults to expecting ISO8601 format
2014-09-02T08:20:07


See Date Formatting below for more examples.

日期和时间查询

Once you have a date/time or date object, you can extract information from it with the following functions. For both date and datetime objects, you can obtain the year, month, day, and so on:

julia> Dates.year(birthday)
1997

julia> Dates.year(datetoday)
2014

julia> Dates.month(birthday)
3

julia> Dates.month(datetoday)
9

julia> Dates.day(birthday)
15

julia> Dates.day(datetoday)
2


and, for date/time objects:

julia> Dates.minute(now())
37

julia> Dates.hour(now())
16

julia> Dates.second(now())
8

julia> Dates.minute(rightnow)
37

julia> Dates.hour(rightnow)
16

julia> Dates.second(rightnow)
8


There's also a bunch of other useful ones:

julia> Dates.dayofweek(birthday)
6

julia> Dates.dayname(birthday)
"Saturday"

julia> Dates.yearmonth(now())
(2014,9)

julia> Dates.yearmonthday(birthday)
(1997,3,15)

julia> Dates.isleapyear(birthday)
false

julia> Dates.daysofweekinmonth(datetoday)
5

julia> Dates.monthname(birthday)
"March"

julia> Dates.monthday(now())
(9,2)

julia> Dates.dayofweekofmonth(birthday)
3


Two of those functions are very similarly named: the Dates.daysofweekinmonth() (days of week in month) function tells you how many days there are in the month with the same day name as the specified day — there are five Tuesdays in the current month (at the time of writing). The last function, dayofweekofmonth(birthday) (day of week of month), tells us that the 15th of March, 1997, was the third Saturday of the month.

You can also find days relative to a date, such as the first day of the week containing that day, using the adjusting functions, described below.

日期计算

You can do arithmetic on dates and date/time objects. Subtracting two dates or datetimes to find the difference is the most obvious one:

julia> datetoday - birthday
6380 days

julia> datetimenow - armistice
3023472252000 milliseconds


which you can convert to Dates.Days or Dates.Milliseconds or some other unit:

julia> Dates.Period(datetoday - birthday)
7357 days

julia> Dates.canonicalize(Dates.CompoundPeriod(datetimenow - armistice))
5138 weeks, 5 days, 5 hours, 46 minutes, 1 second, 541 milliseconds

julia> convert(Dates.Day, Dates.Period(Dates.today() - Dates.Date(2016, 1, 1)))
491 days

julia> convert(Dates.Millisecond, Dates.Period(Dates.today() - Dates.Date(2016, 1, 1)))
42422400000 milliseconds


To add and subtract periods of time to date and date/time objects, use the Dates. constructor functions to specify the period. For example, Dates.Year(20) defines a period of 20 years, and Dates.Month(6) defines a period of 6 months. So, to add 20 years and 6 months to the birthday date:

julia> birthday + Dates.Year(20) + Dates.Month(6)
2017-09-15


Here's 6 months ago from now:

julia> Dates.now() - Dates.Month(6)
2014-03-02T16:43:08


and similarly for months, weeks:

julia> Dates.now() - Dates.Year(2) - Dates.Month(6)
2012-03-02T16:44:03


and similarly for weeks and hours. Here's the date and time for two weeks and 12 hours from now:

julia> Dates.now() + Dates.Week(2) + Dates.Hour(12)
2015-09-18T20:49:16


and there are

julia> daystoxmas = Dates.Date.(Dates.year(Dates.now()), 12, 25) - Dates.today()
148 days



or 148 (shopping) days till Christmas (at the time this was written).

To retrieve the value as a number, use the function Dates.value():

julia> Dates.value(daystoxmas)
148


This works with different types of date/time objects too:

julia> lastchristmas = Dates.now() - Dates.DateTime(2017, 12, 25, 0, 0, 0)
25464746504 milliseconds

julia> Dates.value(lastchristmas)
25464746504


日期范围

You can make iterable range objects that define a range of dates:

julia>  d = Dates.Date(1980,1,1):Dates.Month(3):Dates.Date(2019,1,1)
1980-01-01:3 months:2019-01-01


This iterator yields the first day of every third month. To find out which of these fall on weekdays, you can provide an anonymous function to filter() that tests the day name against the given day names:

julia> weekdays = filter(dy -> Dates.dayname(dy) != "Saturday" && Dates.dayname(dy) != "Sunday" , d)


104-element Array{Date,1}:

1980-01-01
1980-04-01
1980-07-01
⋮
2014-07-01
2014-10-01
2016-04-01
2016-07-01
2018-01-01
2018-10-01
2019-01-01


Similarly, here's a range of times 3 hours apart from now, for a year hence:

julia> d = collect(Dates.DateTime(Dates.now()):Dates.Hour(3):Dates.DateTime(Dates.now() + Dates.Year(1)))
2929-element Array{DateTime,1}:
2015-09-04T08:30:59
2015-09-04T11:30:59
2015-09-04T14:30:59
⋮
2016-09-03T20:30:59
2016-09-03T23:30:59
2016-09-04T02:30:59
2016-09-04T05:30:59
2016-09-04T08:30:59


If you have to pay a bill every 30 days, starting on the 1st of January 2018, the following code shows how the due date creeps forward every month:

julia> foreach(d -> println(Dates.format(d, "d u yyyy")), Dates.Date("2018-01-01"):Dates.Day(30):Dates.Date("2019-01-01"))
1 Jan 2018
31 Jan 2018
2 Mar 2018
1 Apr 2018
1 May 2018
31 May 2018
30 Jun 2018
30 Jul 2018
29 Aug 2018
28 Sep 2018
28 Oct 2018
27 Nov 2018
27 Dec 2018


日期格式化

To specify date formats, you use date formatting codes in a formatting string. Each character refers to a date/time element:

y  Year digit eg yyyy => 2015, yy => 15
m  Month digit eg m => 3 or 03
u  Month name eg Jan
U  Month name eg January
e  Day of week eg Tue
E  Day of week eg Tuesday
d  Day eg 3 or 03
H  Hour digit eg HH => 00
M  Minute digit eg MM => 00
S  Second digit eg S => 00
s  Millisecond digit eg .000


You can use these formatting strings with functions such as DateTime() and Dates.format(). For example, you create a DateTime object from a string by identifying the different elements in the incoming string:

julia> Dates.Date("Fri, 15 Jun 2018", "e, d u y")
2018-06-15

julia> Dates.DateTime("Fri, 15 Jun 2018 11:43:14", "e, d u y H:M:S")
2018-06-15T11:43:14


Other characters are used literally. In the second example, the formatting characters matched up as follows:

Fri, 15 Jun 2018 11:43:14
e  ,  d   u    y  H: M: S


You can supply a format string to Dates.format to format a date object. In the formatting string, you repeat the characters to control how years and days, for example, are output:

julia> timenow = Dates.now()
2015-07-28T11:43:14

julia> Dates.format(timenow, "e, dd u yyyy HH:MM:SS")
"Tue, 28 Jul 2015 11:43:14"


When you're creating a formatted date, you can double some of the components of the format string to produce a leading zero for single digit date elements:

julia> anothertime = Dates.DateTime("Tue, 8 Jul 2015 2:3:7", "e, d u y H:M:S")
2015-07-08T02:03:07

julia> Dates.format(anothertime, "e: dd u yy, HH.MM.SS") # with leading zeros
"Wed: 08 Jul 15, 02.03.07"

julia> Dates.format(anothertime, "e: d u yy, H.M.S")
"Wed: 8 Jul 15, 2.3.7"


To convert a date string from one format to another, you can use DateTime() and a format string to convert the string to a DateTime object, then DateFormat() to output the object in a different format:

julia> formatted_date = "Tue, 28 Jul 2015 11:43:14"
"Tue, 28 Jul 2015 11:43:14"

julia> temp = Dates.DateTime(formatted_date, "e, dd u yyyy HH:MM:SS")
2015-07-28T11:43:14

julia> Dates.format(temp, "dd, U, yyyy HH:MM, e")
"28, July, 2015 11:43, Tue"


If you're doing a lot of date formatting (you can apply date functions to an array of strings), it's a good idea to pre-define a DateFormat object and then use that for bulk conversions (this is quicker):

julia> dformat = Dates.DateFormat("y-m-d");

julia> Dates.Date.([   # broadcast
"2010-01-01",
"2011-03-23",
"2012-11-3",
"2013-4-13",
"2014-9-20",
"2015-3-1"
], dformat)

6-element Array{Date,1}:
2010-01-01
2011-03-23
2012-11-03
2013-04-13
2014-09-20
2015-03-01



There are some built-in formats that you can use. For example, there's Dates.ISODateTimeFormat to give you the ISO8601 format:

julia> Dates.DateTime.([
"2010-01-01",
"2011-03-23",
"2012-11-3",
"2013-4-13",
"2014-9-20",
"2015-3-1"
], Dates.ISODateTimeFormat)
6-element Array{DateTime,1}:
2010-01-01T00:00:00
2011-03-23T00:00:00
2012-11-03T00:00:00
2013-04-13T00:00:00
2014-09-20T00:00:00
2015-03-01T00:00:00</syntaxhighlight>


and here's good old RFC1123:

julia> Dates.format(Dates.now(), Dates.RFC1123Format)
"Sat, 30 Jul 2016 16:36:09"


日期调整

Sometimes you want to find a date nearest to another - for example, the first day of that week, or the last day of the month that contains that date. You can do this with the functions like Dates.firstdayofweek() and Dates.lastdayofmonth(). So, if we're currently in the middle of the week:

julia> Dates.dayname(now())
"Wednesday"


the first day of the week is returned by this:

julia> Dates.firstdayofweek(now())
2014-09-01T00:00:00


which you could also write using the function chain operator:

julia> Dates.now() |> Dates.firstdayofweek |> Dates.dayname
"Monday"


A more general solution is provided by the tofirst(), tolast(), tonext(), and toprev() methods.

With tonext() and toprev(), you can provide a (possibly anonymous) function that returns true when a date has been correctly adjusted. For example, the function:

d->Dates.dayofweek(d) == Dates.Tuesday


returns true if the day d is a Tuesday. Use this with the tonext() method:

julia> Dates.tonext(d->Dates.dayofweek(d) == Dates.Tuesday, birthday)
1997-03-18 # the first Tuesday after the birthday


Or you can find the next Sunday following the birthday date:

julia> Dates.tonext(d->Dates.dayname(d) == "Sunday", birthday)
1997-03-16 # the first Sunday after the birthday


With tofirst() and tolast(), you can find the first Sunday, or Thursday, or whatever, of a month. Monday is 1, Tuesday 2, etc.

julia> Dates.tofirst(birthday, 1) # the first Monday (1) of that month
1997-03-03


Supply the keyword argument of=Year to get the first matching weekday of the year.

julia> Dates.tofirst(birthday, 1, of=Year) # the first Monday (1) of 1997
1997-01-06


日期和时间的四舍五入

You can use round(), floor(), and ceil(), usually used to round numbers up or down to the nearest preferred values, to adjust dates forward or backwards in time so that they have 'rounder' values.

julia> Dates.now()
2016-09-12T17:55:11.378

julia> Dates.format(round(Dates.DateTime(Dates.now()), Dates.Minute(15)), Dates.RFC1123Format)
"Mon, 12 Sep 2016 18:00:00"


The ceil() adjusts dates or times forward in time:

julia> ceil(birthday, Dates.Month)
1997-04-01

julia> ceil(birthday, Dates.Year)
1998-01-01

julia> ceil(birthday, Dates.Week)
1997-03-17


Recurring dates

It's useful to be able to find all dates in a range of dates that satisfy some particular criteria. For example, you can work out the second Sunday in a month by using the Dates.dayofweekofmonth() and Dates.dayname() functions.

For example, let's create a range of dates from the first of September 2014 until Christmas Day, 2014:

julia> dr = Dates.Date(2014,9,1):Dates.Day(1):Dates.Date(2014,12,25)
2014-09-01:1 day:2014-12-25


Now an anonymous function similar to the ones we used in tonext() earlier finds a selection of those dates in that range that satisfy that function:

julia> filter(d -> Dates.dayname(d) == "Sunday", dr)
16-element Array{Date,1}:
2014-09-07
2014-09-14
2014-09-21
2014-09-28
2014-10-05
2014-10-12
2014-10-19
2014-10-26
2014-11-02
2014-11-09
2014-11-16
2014-11-23
2014-11-30
2014-12-07
2014-12-14
2014-12-21


These are the dates of every Sunday between September 1st 2014 until Christmas Day, 2014.

By combining criteria in the anonymous function, you can build up more complicated recurring events. Here's a list of all the Tuesdays in that period which are on days that are odd numbered and greater than 20:

julia> filter(d->Dates.dayname(d) == "Tuesday" && isodd(Dates.day(d)) && Dates.day(d) > 20, dr)
4-element Array{Date,1}:
2014-09-23
2014-10-21
2014-11-25
2014-12-23


and here's every second Tuesday in 2016 between April and November:

dr = Dates.Date(2015):Dates.Day(1):Dates.Date(2016);
filter(dr) do x
Dates.dayofweek(x) == Dates.Tue &&
Dates.April <= Dates.month(x) <= Dates.Nov &&
Dates.dayofweekofmonth(x) == 2
end

8-element Array{Base.Dates.Date,1}:
2015-04-14
2015-05-12
2015-06-09
2015-07-14
2015-08-11
2015-09-08
2015-10-13
2015-11-10

Unix 时间

You sometimes have to deal with another type of timekeeping: Unix time. Unix time is a count of the number of seconds that have elapsed since the beginning of the year 1970 (the birth of Unix). In Julia the count is stored in a 64 bit integer, and we'll never see the end of Unix time. (The universe will have ended long before 64 bit Unix time reaches the maximum possible value, which will be in approximately 292 billion years from now, at 15:30:08 on Sunday, 4 December 292,277,026,596.)

In Julia, the time() function, used without arguments, returns the Unix time value of the current second:

julia> time()
1.414141581230945e9


The strftime() ("string format time") function, which lives in the Libc module, converts a number of seconds in Unix time to a more readable form:

julia> Libc.strftime(86400 * 365.25 * 4) # 4 years worth of Unix seconds
"Tue  1 Jan 00:00:00 1974"


You can choose a different format by supplying a format string, with the different components of the date and time defined by '%' letter codes:

julia> Libc.strftime("%A, %B %e at %T, %Y", 86400 * 365.25 * 4)
"Tuesday, January  1 at 00:00:00, 1974"


The strptime() function takes a format string and a date string, and returns a TmStruct expression. This can then be converted to a Unix time value by passing it to time():

julia> Libc.strptime("%A, %B %e at %T, %Y", "Tuesday, January  1 at 00:00:00, 1974")
Base.Libc.TmStruct(0,0,0,1,0,74,2,0,0,0,0,0,0,0)

julia> time(ans)
1.262304e8

julia> time(Libc.strptime("%Y-%m-%d","2014-10-1"))
1.4121216e9


The Dates module also offers a unix2datetime() function, which converts a Unix time value to a date/time object:

julia> Dates.unix2datetime(time())
2014-10-24T09:26:29.305


时刻

DateTimes are stored as milliseconds. You can access the field instant to see the actual value.

julia> moment=Dates.now()
2017-02-01T12:45:46.326

julia> moment.instant
Base.Dates.UTInstant{Base.Dates.Millisecond}(63621636346326 milliseconds)

julia> Dates.value(moment)
63621636346326


If you use the more precise Dates.Time type, you can access nanoseconds.

julia> moment = Dates.Time(Dates.now())
17:38:44.33

julia> moment.instant
63524330000000 nanoseconds

julia> Dates.value(moment)
63524330000000


计时与监测

The @elapsed macro returns the number of seconds an expression took to evaluate:

function test(n)
for i in 1:n
x = sin(rand())
end
end

julia> @elapsed test(100000000)
1.309819509


The @time macro tells you how long an expression took to evaluate, and how memory was allocated.

julia> @time test(100000000)
2.532941 seconds (4 allocations: 160 bytes)

 « Introducing JuliaWorking with dates and times » Working with text files Plotting

Plotting

 « Introducing JuliaPlotting » Working with dates and times Metaprogramming

绘图

Julia 有许多不同的绘图包，应该会有一款适合您的需要，合您口味。本节是对其中一种绘图包 Plots.jl 的快速介绍，它非常有趣，因为它与许多其他绘图软件包进行了交互。在使用Julia进行绘图之前，请下载并安装一些绘图包 (请先在 REPL 中键入 ] 进入 pkg 模式)

(v1.0) pkg> add Plots PyPlot GR UnicodePlots


julia> using Plots


julia> using Astro # you'll need to add this package with:  add https://github.com/cormullion/Astro.jl
julia> using Dates

julia> days = Dates.datetime2julian.(Dates.DateTime(2018, 1, 1, 0, 0, 0):Dates.Day(1):Dates.DateTime(2018, 12, 31, 0, 0, 0))
julia> eq_values = map(equation_time, days)


365-element Array{Float64,1}:
-3.12598
-3.59633
-4.97289
-5.41857
-5.85688
⋮
-1.08709
-1.57435
-2.05845
-2.53887
-3.01508


julia> plot(eq_values)


julia> unicodeplots()

julia> plot(eq_values)

       +------------------------------------------------------------+
17 |                                                ,--u        | y1
|                                              ./   "\       |
|                                             ./      \      |
|                                            ./        .     |
|                                            /         \.    |
|                                           /           \    |
|                                          .           ",   |
|                                         ,             \   |
|                   .r--\.                /               .  |
|                  /    \.              /                \  |
|----------------nr-------fhr-----------v------------------v*|
|               .F          \.         ,                  |.|
|,              /            \,       ,/                    |
|l             /              "\.    ,                      |
|".           /                 \-ur/                       |
| \.         /                                              |
|  l        ,                                               |
|   \      ./                                                |
|   "\.  ./                                                 |
-15 |     '--"                                                   |
+------------------------------------------------------------+
0                                                          370


GR 引擎/后端 也是一个很好的通用绘图软件包：

julia> gr();
julia> plot(eq_values)


绘制函数图像

Switch back to using PyPlot back-end:

julia> pyplot()


The Equation of Time graph can be approximately modeled by a function combining a couple of sine functions:

julia> equation(d) = -7.65 * sind(d) + 9.87 * sind(2d + 206);


It's easy to plot this function for every day of a year. Pass the function to plot(), and use a range to specify the start and end values:

julia> plot(equation, 1:365)


To combine the two plots, so as to compare the equation and the calculated series versions, Plots lets you add another plot to an existing one. The usual Julia convention of using "!" to modify the argument is available here (in an implicit way — you don't actually have to provide the current plot as an argument): the second plot function, plot!() modifies the previous plot:

julia> plot(eq_values);

julia> plot!(equation, 1:365)


自定义绘图

There is copious documentation for the Plots.jl package, and after studying it you'll be able to spend hours tweaking and customizing your plots to your heart's content. Here are a few examples.

The ticks along the x-axis show the numbers from 1:365, derived automatically from the single series provided. It would be better to see the dates themselves. First, create the strings:

julia> days = Dates.DateTime(2018, 1, 1, 0, 0, 0):Dates.Day(1):Dates.DateTime(2018, 12, 31, 0, 0, 0)
julia> datestrings = Dates.format.(days, "u dd")


The supplied value for the xticks option is a tuple consisting of two arrays/ranges:

(xticks = (1:14:366, datestrings[1:14:366])


the first provides the numerical values, the second provides matching text labels for the ticks.

Extra labels and legends are easily added, and you can access colors from the Colors.jl package. Here's a prettier version of the basic plot:

julia>  plot!(
eq_values,

label  = "equation of time (calculated)",
line=(:black, 0.5, 6, :solid),

size=(800, 600),

xticks = (1:14:366, datestrings[1:14:366]),
yticks = -20:2.5:20,

ylabel = "Minutes faster or slower than GMT",
xlabel = "day in year",

title  = "The Equation of Time",

fillrange = 0,
fillalpha = 0.25,
fillcolor = :lightgoldenrod,

background_color = :ivory
)


其他包

UnicodePlots

If you work in the REPL a lot, perhaps you want a quick and easy way to draw plots that use text rather than graphics for output? The UnicodePlots.jl package uses Unicode characters to draw various plots, avoiding the need to load various graphic libraries. It can produce:

• scatter plots
• line plots
• bar plots (horizontal)
• staircase plots
• histograms (horizontal)
• sparsity patterns
• density plots

pkg> add UnicodePlots


You have to do this just once. Now you load the module and import the functions:

julia> using UnicodePlots


Here is a quick example of a line plot:

julia> myPlot = lineplot([1, 2, 3, 7], [1, 2, -5, 7], title="My Plot", border=:dotted)

                       My Plot
⡤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⢤
10 ⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⡇⠀⠀⠀⠀⠔⠒⠊⠉⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⡇⠉⠉⠉⠉⠉⠉⠉⠉⠉⠫⡉⠉⠉⠉⠉⠉⢉⠝⠋⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠁⢸
⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⢀⡠⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
-10 ⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⠓⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠚
0                                       10


And here's a density plot:

julia> myPlot = densityplot(collect(1:100), randn(100), border=:dotted)
⡤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⢤
10 ⡇                                        ⢸
⡇                                        ⢸
⡇                                        ⢸
⡇                                        ⢸
⡇                                        ⢸
⡇                                        ⢸
⡇                                        ⢸
⡇                            ░           ⢸
⡇ ░░░        ░ ▒░  ▒░     ░  ░ ░ ░ ░   ░ ⢸
⡇░░  ░▒░░▓▒▒ ▒░░ ▓░░ ░░░▒░ ░ ░   ▒ ░ ░▒░░⢸
⡇▓▒█▓▓▒█▓▒▒▒█▒▓▒▓▒▓▒▓▓▒▓▒▓▓▓█▒▒█▓▒▓▓▓▓▒▒▒⢸
⡇    ░     ░         ░░░ ░    ▒ ░ ░ ░░ ░ ⢸
⡇                          ░             ⢸
⡇                                        ⢸
⡇                                        ⢸
⡇                                        ⢸
⡇                                        ⢸
⡇                                        ⢸
⡇                                        ⢸
-10 ⡇                                        ⢸
⠓⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠚
0                                      100


(Note that it needs the terminal environment for the displayed graphs to be 100% successful - when you copy and paste, some of the magic is lost.)

VegaLite

allows you to create visualizations in a web browser window. VegaLite is a visualization grammar, a declarative format for creating and saving visualization designs. With VegaLite you can describe data visualizations in a JSON format, and generate interactive views using either HTML5 Canvas or SVG. You can produce:

• Area plots
• Bar plots/Histograms
• Line plots
• Scatter plots
• Pie/Donut charts
• Waterfall charts
• Wordclouds

To use VegaLite, first add the package to your Julia installation. You have to do this just once:

pkg> add VegaLite


Here's how to create a stacked area plot.

julia> using VegaLite
julia> x = [0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9]
julia> y = [28, 43, 81, 19, 52, 24, 87, 17, 68, 49, 55, 91, 53, 87, 48, 49, 66, 27, 16, 15]
julia> g = [0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1]

julia> a = areaplot(x = x, y = y, group = g, stacked = true)


A general feature of VegaLite is that you can modify a visualization after you've created it. So, let's change the color scheme using a function (notice the "!" to indicate that the arguments are modified):

julia> colorscheme!(a, ("Reds", 3))


You can create pie (and donut) charts easily by supplying two arrays. The x array provides the labels, the y array provides the quantities:

julia> fruit = ["peaches", "plums", "blueberries", "strawberries", "bananas"];
julia> bushels = [100, 32, 180, 46, 21];
julia> piechart(x = fruit, y = bushels, holesize = 125)


 « Introducing JuliaPlotting » Working with dates and times Metaprogramming

Metaprogramming

 « Introducing JuliaMetaprogramming » Plotting Modules and packages

何为元编程?

- @time 宏:

julia> @time [sin(cos(i)) for i in 1:100000];
0.102967 seconds (208.65 k allocations: 9.838 MiB)


@time 宏在代码的前面插入了 "秒表开始" 的命令在传入的表达式之前。当代码结束的时候，添加了一个“秒表结束” 的命令。然后进行计算，以报告所经过的时间和内存使用情况。

- @which

julia> @which 2 + 2
+(x::T, y::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at int.jl:53


冒号表达式（Quoted expressions）

julia> x = 3
3

julia> :x
:x


(如果您不熟悉编程中引用符号（Quoted Symbols）的用途，请想想在书写中如何使用引用来区分普通用途和特殊用途。例如，在句子中：

'Copper' contains six letters.

julia> :(2 + 2)
:(2 + 2)


quote
2 + 2
end


quote
#= REPL[123]:2 =#
2 + 2
end

expression = quote
for i = 1:10
println(i)
end
end


quote
#= REPL[124]:2 =#
for i = 1:10
#= REPL[124]:3 =#
println(i)
end
end

expression 对象的类型是 Expr:

julia> typeof(expression)
Expr


对表达式进行求值

Julia 还有一个函数 eval() 用于计算未求值的表达式：

julia> eval(:x)
3
julia> eval(:(2 + 2))
4
julia> eval(expression)
1
2
3
4
5
6
7
8
9
10


e = :(
for i in 1:10
println(i)
end
)


:(for i = 1:10 # line 2:
println(i)
end)

julia> eval(e)
1
2
3
4
5
6
7
8
9
10


表达式的内部（Inside Expressions）

P = quote
a = 2
b = 3
c = 4
d = 5
e = sum([a,b,c,d])
end


quote
#= REPL[125]:2 =#
a = 2
#= REPL[125]:3 =#
b = 3
#= REPL[125]:4 =#
c = 4
#= REPL[125]:5 =#
d = 5
#= REPL[125]:6 =#
e = sum([a, b, c, d])
end

julia> fieldnames(typeof(P))


head字段为:block , args字段是一个数组，包含表达式(包括注释)。我们可以用这些简单的 Julia 技巧来检查这些。

julia> P.args[2]
:(a = 2)


for (n, expr) in enumerate(P.args)
println(n, ": ", expr)
end

1: #= REPL[125]:2 =#
2: a = 2
3: #= REPL[125]:3 =#
4: b = 3
5: #= REPL[125]:4 =#
6: c = 4
7: #= REPL[125]:5 =#
8: d = 5
9: #= REPL[125]:6 =#
10: e = sum([a, b, c, d])

julia> eval(P)
14

julia> P.args[end] = quote prod([a,b,c,d]) end
quote
#= REPL[133]:1 =#
prod([a, b, c, d])
end

julia> eval(P)
120


julia> P.args[end].args[end].args[1]
:sum

julia> P.args[end].args[end].args[1] = :prod
:prod

julia> eval(P)
120


抽象语法树（AST）

julia> dump(:(1 * sin(pi/2)))

 Expr
args: Array{Any}((3,))
1: Symbol *
2: Int64 1
3: Expr
args: Array{Any}((2,))
1: Symbol sin
2: Expr
args: Array{Any}((3,))
1: Symbol /
2: Symbol pi
3: Int64 2
typ: Any
typ: Any
typ: Any

表达式插值

"the sine of 1 is 0.8414709848078965"


 julia> quote s = $(sin(1) + cos(1)); end  quote # none, line 1: s = 1.3817732906760363 end 尽管这是一个被引用（quoted）了的表达式，因此未被计算，但表达式中的 sin(1) + cos(1) 却是被执行了，它的值被插入到了表达式中，原始代码则被值替换了。这种操作称为“拼接”。 与字符串插值一样，只有当你想要插入一个表达式的值时候才需要使用圆括号，插入单个符号的值用$ 就行。

宏

macro p(n)
if typeof(n) == Expr
println(n.args)
end
return n
end


julia> @p 3
3


julia> @p 3 + 4 - 5 * 6 / 7 % 8
Any[:-,:(3 + 4),:(((5 * 6) / 7) % 8)]
2.7142857142857144


Also notice that the macro p returned the argument, which was then evaluated, hence the 2.7142857142857144. But it doesn't have to — it could return a quoted expression instead.

As an example, the built-in @time macro returns a quoted expression rather than using eval() to evaluate the expression inside the macro. The quoted expression returned by @time is evaluated in the calling context when the macro has done its work. Here's the definition:

macro time(ex)
quote
local t0 = time()
local val = $(esc(ex)) local t1 = time() println("elapsed time: ", t1-t0, " seconds") val end end  Notice the $(esc(ex)) expression. This is the way that you 'escape' the code you want to time, which is in ex, so that it isn't evaluated in the macro, but left intact until the entire quoted expression is returned to the calling context and executed there. If this just said $ex, then the expression would be interpolated and evaluated immediately. If you want to pass a multi-line expression to a macro, use the  begin ...  end form: @p begin 2 + 2 - 3 end  Any[:( # none, line 2:),:((2 + 2) - 3)] 1 (You can also call macros with parentheses similar to the way you do when calling functions, using the parentheses to enclose the arguments: julia> @p(2 + 3 + 4 - 5) Any[:-,:(2 + 3 + 4),5] 4  This would allow you to define macros that accepted more than one expression as arguments.) eval() and @eval There's an eval() function, and an @eval macro. You might be wondering what's the difference between the two? julia> ex = :(2 + 2) :(2 + 2) julia> eval(ex) 4 julia> @eval ex :(2 + 2)  The function version (eval()) expands the expression and evaluates it. The macro version doesn't expand the expression you supply to it automatically, but you can use the interpolation syntax to evaluate the expression and pass it to the macro. julia> @eval$(ex)
4


In other words:

julia> @eval $(ex) == eval(ex) true  Here's an example where you might want to create some variables using some automation. We'll create the first ten squares and ten cubes, first using eval(): for i in 1:10 symbolname = Symbol("var_squares_$(i)")
eval(quote $symbolname =$(i^2) end)
end


which creates a load of variables named var_squares_n, such as:

julia> var_squares_5
25


and then using @eval:

for i in 1:10
symbolname = Symbol("var_cubes_$(i)") @eval$symbolname = $(i^3) end  which similarly creates a load of variables named var_cubes_n, such as: julia> var_cubes_5 125  Once you feel confident, you might prefer to write like this: julia> [@eval$(Symbol("var_squares_$(i)")) = ($i^2) for i in 1:10]


Scope and context

When you use macros, you have to keep an eye out for scoping issues. In the previous example, the $(esc(ex)) syntax was used to prevent the expression from being evaluated in the wrong context. Here's another contrived example to illustrate this point. macro f(x) quote s = 4 (s,$(esc(s)))
end
end


This macro declares a variable s, and returns a quoted expression containing s and an escaped version of s.

Now, outside the macro, declare a symbol s:

julia> s = 0


Run the macro:

julia> @f 2
(4,0)


You can see that the macro returned different values for the symbol s: the first was the value inside the macro's context, 4, the second was an escaped version of s, that was evaluated in the calling context, where s has the value 0. In a sense, esc() has protected the value of s as it passes unharmed through the macro. For the more realistic @time example, it's important that the expression you want to time isn't modified in any way by the macro.

Expanding macros

To see what the macro expands to just before it's finally executed, use the macroexpand() function. It expects a quoted expression containing one or more macro calls, which are then expanded into proper Julia code for you so that you can see what the macro would do when called.

julia> macroexpand(Main, quote @p 3 + 4 - 5 * 6 / 7 % 8 end)
Any[:-,:(3 + 4),:(((5 * 6) / 7) % 8)]
quote
#= REPL[158]:1 =#
(3 + 4) - ((5 * 6) / 7) % 8
end


(The #none, line 1: is a filename and line number reference that's more useful when used inside a source file than when you're using the REPL.)

Here's another example. This macro adds a dotimes construction to the language.

macro dotimes(n, body)
quote
for i = 1:$(esc(n))$(esc(body))
end
end
end


This is used as follows:

julia> @dotimes 3 println("hi there")
hi there
hi there
hi there


Or, less likely, like this:

julia> @dotimes 3 begin
for i in 4:6
println("i is $i") end end  i is 4 i is 5 i is 6 i is 4 i is 5 i is 6 i is 4 i is 5 i is 6  If you use macroexpand() on this, you can see what happens to the symbol names: macroexpand(Main, # we're working in the Main module quote @dotimes 3 begin for i in 4:6 println("i is$i")
end
end
end
)


with the following output:

quote
#= REPL[160]:3 =#
begin
#= REPL[159]:3 =#
for #101#i = 1:3
#= REPL[159]:4 =#
begin
#= REPL[160]:4 =#
for i = 4:6
#= REPL[160]:5 =#
println("i is $(i)") end end end end end  The i local to the macro itself has been renamed to #101#i, so as not to clash with the original i in the code we passed to it. A more useful example: @until Here's how to define a macro that is more likely to be useful in your code. Julia doesn't have an until condition ... do some stuff ... end statement. Perhaps you'd like to type something like this: until x > 100 println(x) end  You'll be able to write your code using the new until macro like this: until <condition> <block_of_stuff> end  but, behind the scenes, the work will be done by actual code with the following structure: while true <block_of_stuff> if <condition> break end end  This forms the body of the new macro, and it will be enclosed in a quote ... end block, like this, so that it executes when evaluated, but not before: quote while true <block_of_stuff> if <condition> break end end end  So the nearly-finished macro code is like this: macro until(<condition>, <block_of_stuff>) quote while true <block_of_stuff> if <condition> break end end end end  All that remains to be done is to work out how to pass in our code for the <block_of_stuff> and the <condition> parts of the macro. Recall that $(esc(...)) allows code to pass through 'escaped' (i.e. unevaluated). We'll protect the condition and block code from being evaluated before the macro code runs.

The final macro definition is therefore:

macro until(condition, block)
quote
while true
$(esc(block)) if$(esc(condition))
break
end
end
end
end


The new macro is used like this:

julia> i = 0
0

julia> @until i == 10 begin
global i += 1
println(i)
end

1
2
3
4
5
6
7
8
9
10


or

julia> x = 5
5

julia> @until x < 1 (println(x); global x -= 1)
5
4
3
2
1


Under the hood

If you want a more complete explanation of the compilation process than that provided here, visit the links shown in Further Reading, below.

Julia performs multiple 'passes' to transform your code to native assembly code. As described above, the first pass parses the Julia code and builds the 'surface-syntax' AST, suitable for manipulation by macros. A second pass lowers this high-level AST into an intermediate representation, which is used by type inference and code generation. In this intermediate AST format all macros have been expanded and all control flow has been converted to explicit branches and sequences of statements. At this stage the Julia compiler attempts to determine the types of all variables so that the most suitable method of a generic function (which can have many methods) is selected.

延伸阅读

• Julia Introspects（英文） Leah Hanson 2013 写的关于 Julia 内部表示的文章
 « Introducing JuliaMetaprogramming » Plotting Modules and packages