-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Based on #16580, also much work done by quinnj. `(a=1, ...)` syntax is implemented, and `(; ...)` syntax is implemented but not yet enabled.
- Loading branch information
1 parent
f92817a
commit ebfb307
Showing
23 changed files
with
715 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
# This file is a part of Julia. License is MIT: https://julialang.org/license | ||
|
||
""" | ||
NamedTuple{names,T}(args::Tuple) | ||
Construct a named tuple with the given `names` (a tuple of Symbols) and field types `T` | ||
(a `Tuple` type) from a tuple of values. | ||
""" | ||
function NamedTuple{names,T}(args::Tuple) where {names, T <: Tuple} | ||
if length(args) == length(names) | ||
if @generated | ||
N = length(names) | ||
types = T.parameters | ||
Expr(:new, :(NamedTuple{names,T}), Any[ :(convert($(types[i]), args[$i])) for i in 1:N ]...) | ||
else | ||
N = length(names) | ||
NT = NamedTuple{names,T} | ||
types = T.parameters | ||
fields = Any[ convert(types[i], args[i]) for i = 1:N ] | ||
ccall(:jl_new_structv, Any, (Any, Ptr{Void}, UInt32), NT, fields, N)::NT | ||
end | ||
else | ||
throw(ArgumentError("Wrong number of arguments to named tuple constructor.")) | ||
end | ||
end | ||
|
||
""" | ||
NamedTuple{names}(args::Tuple) | ||
Construct a named tuple with the given `names` (a tuple of Symbols) from a tuple of | ||
values. | ||
""" | ||
function NamedTuple{names}(args::Tuple) where {names} | ||
NamedTuple{names,typeof(args)}(args) | ||
end | ||
|
||
""" | ||
NamedTuple{names}(nt::NamedTuple) | ||
Construct a named tuple by selecting fields in `names` (a tuple of Symbols) from | ||
another named tuple. | ||
""" | ||
function NamedTuple{names}(nt::NamedTuple) where {names} | ||
if @generated | ||
types = Tuple{(fieldtype(nt, n) for n in names)...} | ||
Expr(:new, :(NamedTuple{names, $types}), Any[ :(getfield(nt, $(QuoteNode(n)))) for n in names ]...) | ||
else | ||
types = Tuple{(fieldtype(typeof(nt), n) for n in names)...} | ||
NamedTuple{names, types}(Tuple(getfield(nt, n) for n in names)) | ||
end | ||
end | ||
|
||
NamedTuple() = NamedTuple{(),Tuple{}}(()) | ||
|
||
length(t::NamedTuple) = nfields(t) | ||
start(t::NamedTuple) = 1 | ||
done(t::NamedTuple, iter) = iter > nfields(t) | ||
next(t::NamedTuple, iter) = (getfield(t, iter), iter + 1) | ||
endof(t::NamedTuple) = nfields(t) | ||
getindex(t::NamedTuple, i::Int) = getfield(t, i) | ||
getindex(t::NamedTuple, i::Symbol) = getfield(t, i) | ||
indexed_next(t::NamedTuple, i::Int, state) = (getfield(t, i), i+1) | ||
|
||
convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names,T}) where {names,T} = nt | ||
convert(::Type{NamedTuple{names}}, nt::NamedTuple{names}) where {names} = nt | ||
|
||
function convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names}) where {names,T} | ||
NamedTuple{names,T}(T(nt)) | ||
end | ||
|
||
function show(io::IO, t::NamedTuple) | ||
n = nfields(t) | ||
for i = 1:n | ||
# if field types aren't concrete, show full type | ||
if typeof(getfield(t, i)) !== fieldtype(typeof(t), i) | ||
show(io, typeof(t)) | ||
print(io, "(") | ||
show(io, Tuple(t)) | ||
print(io, ")") | ||
return | ||
end | ||
end | ||
if n == 0 | ||
print(io, "NamedTuple()") | ||
else | ||
print(io, "(") | ||
for i = 1:n | ||
print(io, fieldname(typeof(t),i), " = "); show(io, getfield(t, i)) | ||
if n == 1 | ||
print(io, ",") | ||
elseif i < n | ||
print(io, ", ") | ||
end | ||
end | ||
print(io, ")") | ||
end | ||
end | ||
|
||
eltype(::Type{NamedTuple{names,T}}) where {names,T} = eltype(T) | ||
|
||
==(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = Tuple(a) == Tuple(b) | ||
==(a::NamedTuple, b::NamedTuple) = false | ||
|
||
isequal(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = isequal(Tuple(a), Tuple(b)) | ||
isequal(a::NamedTuple, b::NamedTuple) = false | ||
|
||
_nt_names(::NamedTuple{names}) where {names} = names | ||
_nt_names(::Type{T}) where {names,T<:NamedTuple{names}} = names | ||
|
||
hash(x::NamedTuple, h::UInt) = xor(object_id(_nt_names(x)), hash(Tuple(x), h)) | ||
|
||
isless(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = isless(Tuple(a), Tuple(b)) | ||
# TODO: case where one argument's names are a prefix of the other's | ||
|
||
same_names(::NamedTuple{names}...) where {names} = true | ||
same_names(::NamedTuple...) = false | ||
|
||
function map(f, nt::NamedTuple{names}, nts::NamedTuple...) where names | ||
if !same_names(nt, nts...) | ||
throw(ArgumentError("Named tuple names do not match.")) | ||
end | ||
# this method makes sure we don't define a map(f) method | ||
NT = NamedTuple{names} | ||
if @generated | ||
N = length(names) | ||
M = length(nts) | ||
args = Expr[:(f($(Expr[:(getfield(nt, $j)), (:(getfield(nts[$i], $j)) for i = 1:M)...]...))) for j = 1:N] | ||
:( NT(($(args...),)) ) | ||
else | ||
NT(map(f, map(Tuple, (nt, nts...))...)) | ||
end | ||
end | ||
|
||
# a version of `in` for the older world these generated functions run in | ||
@pure function sym_in(x::Symbol, itr::Tuple{Vararg{Symbol}}) | ||
for y in itr | ||
y === x && return true | ||
end | ||
return false | ||
end | ||
|
||
@pure function merge_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}}) | ||
names = Symbol[an...] | ||
for n in bn | ||
if !sym_in(n, an) | ||
push!(names, n) | ||
end | ||
end | ||
(names...,) | ||
end | ||
|
||
@pure function merge_types(names::Tuple{Vararg{Symbol}}, a::Type{<:NamedTuple}, b::Type{<:NamedTuple}) | ||
bn = _nt_names(b) | ||
Tuple{Any[ fieldtype(sym_in(n, bn) ? b : a, n) for n in names ]...} | ||
end | ||
|
||
""" | ||
merge(a::NamedTuple, b::NamedTuple) | ||
Construct a new named tuple by merging two existing ones. | ||
The order of fields in `a` is preserved, but values are taken from matching | ||
fields in `b`. Fields present only in `b` are appended at the end. | ||
```jldoctest | ||
julia> merge((a=1, b=2, c=3), (b=4, d=5)) | ||
(a = 1, b = 4, c = 3, d = 5) | ||
``` | ||
""" | ||
function merge(a::NamedTuple{an}, b::NamedTuple{bn}) where {an, bn} | ||
if @generated | ||
names = merge_names(an, bn) | ||
types = merge_types(names, a, b) | ||
vals = Any[ :(getfield($(sym_in(n, bn) ? :b : :a), $(QuoteNode(n)))) for n in names ] | ||
:( NamedTuple{$names,$types}(($(vals...),)) ) | ||
else | ||
names = merge_names(an, bn) | ||
types = merge_types(names, typeof(a), typeof(b)) | ||
NamedTuple{names,types}(map(n->getfield(sym_in(n, bn) ? b : a, n), names)) | ||
end | ||
end | ||
|
||
merge(a::NamedTuple{()}, b::NamedTuple) = b | ||
|
||
""" | ||
merge(a::NamedTuple, iterable) | ||
Interpret an iterable of key-value pairs as a named tuple, and perform a merge. | ||
```jldoctest | ||
julia> merge((a=1, b=2, c=3), [:b=>4, :d=>5]) | ||
(a = 1, b = 4, c = 3, d = 5) | ||
``` | ||
""" | ||
function merge(a::NamedTuple, itr) | ||
names = Symbol[] | ||
vals = Any[] | ||
inds = ObjectIdDict() | ||
for (k,v) in itr | ||
oldind = get(inds, k, 0) | ||
if oldind > 0 | ||
vals[oldind] = v | ||
else | ||
push!(names, k) | ||
push!(vals, v) | ||
inds[k] = length(names) | ||
end | ||
end | ||
merge(a, NamedTuple{(names...,)}((vals...,))) | ||
end | ||
|
||
keys(nt::NamedTuple{names}) where {names} = names | ||
values(nt::NamedTuple) = Tuple(nt) | ||
haskey(nt::NamedTuple, key::Union{Integer, Symbol}) = isdefined(nt, key) | ||
get(nt::NamedTuple, key::Union{Integer, Symbol}, default) = haskey(nt, key) ? getfield(nt, key) : default | ||
get(f::Callable, nt::NamedTuple, key::Union{Integer, Symbol}) = haskey(nt, key) ? getfield(nt, key) : f() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.