如何在 Julia 中获取 `do` 块的值?

How do get values of out a `do` block in Julia?

我有一个 HDF5 文件,我想从中读取 2 个数组。我怎样才能使用 do 块表示法得到它们?

using HDF5

function myfunc()
    h5open("path", "r") do f
        a = read(f, "a")
        b = read(f, "b")
    end

    # ... do some more processing of a, b
    return a, b
end

如果我运行那个,它会在a not defined的do块之后出错。我如何获取这些值以便我可以在之后处理它们,而不用将完整的计算包装在 do 块中?

A do 块只是用于创建作为第一个参数传递的匿名函数的语法(在本例中为 h5open)。就像常规函数一样,您需要 return 来自要在“外部”使用的匿名函数的任何值:

# Function to mimic Base.open, HDF5.h5open etc
function open(f)
    return f()
end

function g()
    a, b = open() do
        c = "hello"
        d = "world"
        return c, d # returns from the "do"-anonymous function
    end
    return a, b
end

您可以像这样在外部函数中定义 local 变量:

julia> function f()
           local a, b
           map(10) do x
               a = x + 1
               b = x + 2
           end
           return a, b
       end
f (generic function with 1 method)

julia> f()
(11, 12)

这种方法的缺点是编译器无法在所有情况下使其类型稳定,并且还可能装箱 ab(这两种情况都发生在我的示例中)。 @fredrikekre 提出的是一种首选方法 AFAICT。参见:

julia> @code_warntype f()
Variables
  #self#::Core.Compiler.Const(f, false)
  b@_2::Core.Box
  a@_3::Core.Box
  #1::var"#1#2"
  a@_5::Union{}
  b@_6::Union{}

Body::Tuple{Any,Any}
1 ─       (b@_2 = Core.Box())
│         (a@_3 = Core.Box())
│         (#1 = %new(Main.:(var"#1#2"), b@_2, a@_3))
│   %4  = #1::var"#1#2"
│         Main.map(%4, 10)
│   %6  = Core.isdefined(a@_3, :contents)::Bool
└──       goto #3 if not %6
2 ─       goto #4
3 ─       Core.NewvarNode(:(a@_5))
└──       a@_5
4 ┄ %11 = Core.getfield(a@_3, :contents)::Any
│   %12 = Core.isdefined(b@_2, :contents)::Bool
└──       goto #6 if not %12
5 ─       goto #7
6 ─       Core.NewvarNode(:(b@_6))
└──       b@_6
7 ┄ %17 = Core.getfield(b@_2, :contents)::Any
│   %18 = Core.tuple(%11, %17)::Tuple{Any,Any}
└──       return %18