在 Julia 中设置多个 I/O 缓冲区的最佳方法是什么?

What is the best way to set up multiple I/O buffers in Julia?

我正在试验在 Julia 中处理长文本字符串(例如书的长度)的不同方法。具体来说,我正在研究转置密码,并一直在使用 (1) 字符串连接、(2) 数组和 (3) I/O 缓冲区测试速度和内存使用情况。在最后一种情况下,我需要能够 'print' 单个字符到不同的、可索引的 I/O 缓冲区。我的第一次(简化)尝试如下:

text = fill(IOBuffer(), 3)
print(text[1], 'a')
print(text[2], 'b')
print(text[3], 'c')
for i in 1:3
    println(String(take!(text[i])))
end

这会产生:

"abc"
""
""

换句话说,第一个索引返回整个字符串 "abc" 而不仅仅是所需的字符 'a',而其他索引在缓冲区被重置时生成空字符串 ""在第一个 take!() 函数之后。

我的下一次尝试成功了,但似乎并不复杂:

text = Vector(3)
for i in 1:3
    text[i] = IOBuffer()
end
print(text[1], 'a')
print(text[2], 'b')
print(text[3], 'c')
for i in 1:3
    println(String(take!(text[i])))
end

这会产生所需的输出:

"a"
"b"
"c"

我仍然不完全确定为什么第一种方法失败而第二种方法有效,但是有谁知道设置多个 I/O 可以使用索引写入的缓冲区的更好方法吗?

你的问题的原因是在 text = fill(IOBuffer(), 3) 中调用 IOBuffer() 只被评估一次,所以 text 的所有条目都指向同一个对象。可以通过运行ning:

查看
julia> all(x->x===text[1], text)
true

或者当你 运行:

时你可以看到这个
julia> fill(println("AAA"), 3)
AAA
3-element Array{Void,1}:
 nothing
 nothing
 nothing

发现 println 仅被调用一次 - 在将其值传递给 fill 方法之前。

解决它的最简单方法是使用理解:

julia> text = [IOBuffer() for i in 1:3]
3-element Array{Base.AbstractIOBuffer{Array{UInt8,1}},1}:
 IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)
 IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)
 IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)

julia> map(x->x===text[1], text)
3-element Array{Bool,1}:
  true
 false
 false

map(不太干净):

julia> map(i->IOBuffer(), 1:3)
3-element Array{Base.AbstractIOBuffer{Array{UInt8,1}},1}:
 IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)
 IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)
 IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)

julia> map(x->x===text[1], text)
3-element Array{Bool,1}:
  true
 false
 false

实际上你可以用 IOBuffer 类型和 invoke 类型填充数组(这不是推荐的方法,但会告诉你区别):

julia> text = invoke.(fill(IOBuffer, 3), [Tuple{}])
3-element Array{Base.AbstractIOBuffer{Array{UInt8,1}},1}:
 IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)
 IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)
 IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)

julia> map(x->x===text[1], text)
3-element Array{Bool,1}:
  true
 false
 false

最后,如 here 所述,要记住的一点是推导将为创建的数组的每个条目调用一个函数,但如果您使用宏,它只会被调用一次。这是 link 中详细解释的一个简短示例:

julia> rx = [Regex("a") for i in 1:3]
3-element Array{Regex,1}:
 r"a"
 r"a"
 r"a"

julia> map(x->x===rx[1], rx)
3-element Array{Bool,1}:
  true
 false
 false

julia> rx = [r"a" for i in 1:3]
3-element Array{Regex,1}:
 r"a"
 r"a"
 r"a"

julia> map(x->x===rx[1], rx)
3-element Array{Bool,1}:
 true
 true
 true