z = begin
x = 1
y = 2
x + y
end
下边的代码似乎等同:
x = 1
y = 2
z = x + y
begin … end 表达式的实际意义何在?
主要就是方便写
例如
- 贴进 REPL 里,防止它输入一行运行一行产生奇怪的行为
- 方便用一些宏,如
@testset
- 使得一些变量没必要占空间
谢谢谢谢谢谢谢谢谢谢
begin ... end
的作用是把一段代码当成一个整体(block of code)。
julia> x = 1
1
julia> y = 2
2
julia> z = x + y
3
是3个表达式,会返回三次结果。
julia> z = begin
x = 1
y = 2
x + y
end
3
是一个表达式,只会返回一次结果。
这样的好处主要体现在元编程的时候,用 begin ... end
括起来的代码可以很方便地操作,比如下面的代码:
using Plots
julia> @macroexpand @recipe function f(r::Result; ε_max = 0.5)
# set a default value for an attribute with `-->`
xlabel --> "x"
yguide --> "y"
markershape --> :diamond
# add a series for an error band
@series begin
# force an argument with `:=`
seriestype := :path
# ignore series in legend and color cycling
primary := false
linecolor := nothing
fillcolor := :lightgray
fillalpha := 0.5
fillrange := r.y .- r.ε
# ensure no markers are shown for the error band
markershape := :none
# return series data
r.x, r.y .+ r.ε
end
# get the seriescolor passed by the user
c = get(plotattributes, :seriescolor, :auto)
# highlight big errors, otherwise use the user-defined color
markercolor := ifelse.(r.ε .> ε_max, :red, c)
# return data
r.x, r.y
end
:(function (RecipesBase).apply_recipe(plotattributes::AbstractDict{Symbol, Any}, r::Result)
#= /Users/qz/.julia/packages/RecipesBase/BRe07/src/RecipesBase.jl:296 =#
$(Expr(:meta, :nospecialize))
#= /Users/qz/.julia/packages/RecipesBase/BRe07/src/RecipesBase.jl:297 =#
begin
ε_max = get!(plotattributes, :ε_max, 0.5)
end
#= /Users/qz/.julia/packages/RecipesBase/BRe07/src/RecipesBase.jl:298 =#
begin
(RecipesBase).is_key_supported(:ε_max) || delete!(plotattributes, :ε_max)
end
#= /Users/qz/.julia/packages/RecipesBase/BRe07/src/RecipesBase.jl:299 =#
series_list = (RecipesBase).RecipeData[]
#= /Users/qz/.julia/packages/RecipesBase/BRe07/src/RecipesBase.jl:300 =#
func_return = begin
#= REPL[8]:1 =#
#= REPL[8]:3 =#
(RecipesBase).is_explicit(plotattributes, :xlabel) || (plotattributes[:xlabel] = "x")
#= REPL[8]:4 =#
(RecipesBase).is_explicit(plotattributes, :yguide) || (plotattributes[:yguide] = "y")
#= REPL[8]:5 =#
(RecipesBase).is_explicit(plotattributes, :markershape) || (plotattributes[:markershape] = :diamond)
#= REPL[8]:7 =#
begin
#= /Users/qz/.julia/packages/RecipesBase/BRe07/src/RecipesBase.jl:339 =#
let plotattributes = copy(plotattributes)
#= /Users/qz/.julia/packages/RecipesBase/BRe07/src/RecipesBase.jl:340 =#
args = begin
#= REPL[8]:9 =#
plotattributes[:seriestype] = :path
#= REPL[8]:11 =#
plotattributes[:primary] = false
#= REPL[8]:12 =#
plotattributes[:linecolor] = nothing
#= REPL[8]:13 =#
plotattributes[:fillcolor] = :lightgray
#= REPL[8]:14 =#
plotattributes[:fillalpha] = 0.5
#= REPL[8]:15 =#
plotattributes[:fillrange] = r.y .- r.ε
#= REPL[8]:17 =#
plotattributes[:markershape] = :none
#= REPL[8]:19 =#
(r.x, r.y .+ r.ε)
end
#= /Users/qz/.julia/packages/RecipesBase/BRe07/src/RecipesBase.jl:341 =#
push!(series_list, (RecipesBase).RecipeData(plotattributes, (RecipesBase).wrap_tuple(args)))
#= /Users/qz/.julia/packages/RecipesBase/BRe07/src/RecipesBase.jl:345 =#
nothing
end
end
#= REPL[8]:22 =#
c = get(plotattributes, :seriescolor, :auto)
#= REPL[8]:24 =#
plotattributes[:markercolor] = ifelse.(r.ε .> ε_max, :red, c)
#= REPL[8]:26 =#
(r.x, r.y)
end
#= /Users/qz/.julia/packages/RecipesBase/BRe07/src/RecipesBase.jl:301 =#
func_return === nothing || push!(series_list, (RecipesBase).RecipeData(plotattributes, (RecipesBase).wrap_tuple(func_return)))
#= /Users/qz/.julia/packages/RecipesBase/BRe07/src/RecipesBase.jl:308 =#
series_list
end)
你数数里面嵌套了多少个 begin ... end
, begin ... end
之中可以插入任意行数的代码。
所以如果你用过 Pluto.jl
, 就知道它不允许你一个 cell 里面有多个表达式,这时候就必须要把多个表达式用 begin ... end
包裹起来,当成一个表达式。
begin ... end
还很适合和 macros 配套使用,除了上面的 @series begin ... end
, 更常用的是比如 @eval
:
for (op, Ty, Tz) in ((:*, Real, :P),
(:/, :P, Float64), (:/, Real, :P))
@eval begin
function ($op)(X::StridedArray{P}, y::$Ty) where P<:Period
# depwarn("non-broadcasted arithmetic is deprecated for Dates.TimeType; use broadcasting instead", nothing)
Z = similar(X, $Tz)
for (Idst, Isrc) in zip(eachindex(Z), eachindex(X))
@inbounds Z[Idst] = ($op)(X[Isrc], y)
end
return Z
end
end
end
这样写多优雅呀。同样的代码用 Base.eval
来写就会更麻烦一点。
基于同样的原因,可以在 debugging 的时候使用 begin ... end
, 比如我想debug下面这个 onliner:
f(x) = "original code"
就可以用
f(x) = begin
#debugging code
"original code"
end
就是一个比较容易地把单行代码变成多行代码的方法。反正不管你中间怎么改,begin ... end
只返回最后一行的值。
3 个赞