我们说有一种类型
immutable Foo
x :: Int64
y :: Float64
end
并且有一个变量foo = Foo(1,2.0)
。我想使用bar
构建一个新变量foo
作为原型y = 3.0
(或者,非破坏性更新foo
生成一个新的Foo
对象)。在ML语言(Haskell,OCaml,F#)和其他一些语言(例如Clojure)中,伪代码看起来像是一个成语
bar = {foo with y = 3.0}
朱莉娅有这样的事吗?
答案 0 :(得分:4)
这很棘手。在Clojure中,这将使用数据结构,动态类型的不可变映射,因此我们只需调用适当的方法来添加/更改密钥。但是当使用 types 时,我们必须做一些反思来为该类型生成一个合适的新构造函数。此外,与Haskell或各种ML不同,Julia不是静态类型的,所以不要只看一个像{foo with y = 1}
这样的表达式,并找出应该生成什么代码来实现它。
实际上,我们可以为此构建一个Clojure-esque解决方案;因为朱莉娅提供了足够的反思和动力,我们可以将类型视为一种不可变的地图。我们可以使用fieldnames
来获取"键列表"按顺序(如[:x, :y]
),我们可以使用getfield(foo, :x)
动态获取字段值:
immutable Foo
x
y
z
end
x = Foo(1,2,3)
with_slow(x, p) =
typeof(x)(((f == p.first ? p.second : getfield(x, f)) for f in fieldnames(x))...)
with_slow(x, ps...) = reduce(with_slow, x, ps)
with_slow(x, :y => 4, :z => 6) == Foo(1,4,6)
然而,这被称为with_slow
的原因。由于它的反射,它将无法接近,就像withy(foo::Foo, y) = Foo(foo.x, y, foo.z)
这样的手写函数一样快。如果Foo
被参数化(例如Foo{T}
与y::T
),那么Julia将能够推断withy(foo, 1.)
返回Foo{Float64}
,但不会能够推断出with_slow
。我们知道,这会杀死 crab 的表现。
使其与ML和co一样快的唯一方法是生成有效等效于手写版本的代码。碰巧的是,我们也可以推出这个版本!
# Fields
type Field{K} end
Base.convert{K}(::Type{Symbol}, ::Field{K}) = K
Base.convert(::Type{Field}, s::Symbol) = Field{s}()
macro f_str(s)
:(Field{$(Expr(:quote, symbol(s)))}())
end
typealias FieldPair{F<:Field, T} Pair{F, T}
# Immutable `with`
for nargs = 1:5
args = [symbol("p$i") for i = 1:nargs]
@eval with(x, $([:($p::FieldPair) for p = args]...), p::FieldPair) =
with(with(x, $(args...)), p)
end
@generated function with{F, T}(x, p::Pair{Field{F}, T})
:($(x.name.primary)($([name == F ? :(p.second) : :(x.$name)
for name in fieldnames(x)]...)))
end
第一部分是生成类似符号的对象f"foo"
的hack,其值在类型系统中是已知的。生成的函数就像一个宏,它接受类型而不是表达式;因为它可以访问Foo
和字段名称,它可以生成本代码的手动优化版本。如果参数化Foo
,您还可以检查Julia是否能够正确推断输出类型:
@code_typed with(x, f"y" => 4., f"z" => "hello") # => ...::Foo{Int,Float64,String}
(for nargs
行本质上是一个手动展开的reduce,可以启用此功能。)
最后,为了避免被指责给出一些有点疯狂的建议,我想提醒一下,这并不是朱莉娅那种惯用语。虽然我不能在不知道您的用例的情况下提供非常具体的建议,但通常最好的是具有可管理(小)字段集和一小组函数的字段,这些字段对这些字段进行基本操作;您可以在这些函数的基础上构建最终的公共API。如果你想要的东西真的是一个不可改变的字典,你只需要使用专门的数据结构就可以了。
答案 1 :(得分:0)
setindex
包中还实现了!
(最后没有FixedSizeArrays.jl
),这样做有效。