-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
123 additions
and
157 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
|
||
# We could make this a generated function as well, but since the header | ||
# is only printed once at the beginning, the additional time needed for | ||
# runtime introspection is worth the reduction in complexity. | ||
"Print a header for an observation type `stats_t` to `output` using field separator `FS`, name separator `NS` and line separator `LS`." | ||
function print_header(output, stats_t; FS="\t", NS="_", LS="\n") | ||
fn = fieldnames(stats_t) | ||
ft = fieldtypes(stats_t) | ||
|
||
for (i, (name, typ)) in enumerate(zip(fn, ft)) | ||
if typ <: NamedTuple | ||
# aggregate stat | ||
header(output, string(name), string.(fieldnames(typ)), FS, NS) | ||
else | ||
# single stat | ||
print(output, string(name)) | ||
end | ||
|
||
if i < length(fn) | ||
print(output, FS) | ||
end | ||
end | ||
|
||
print(output, LS) | ||
end | ||
|
||
# print header for aggregate stat | ||
function header(out, stat_name, stat_names, FS, NS) | ||
@assert length(stat_names) > 0 | ||
|
||
print(out, join((stat_name * NS) .* stat_names, FS)) | ||
end | ||
|
||
# It's quite possibly overkill to make this a generated function, but we | ||
# don't want anybody accusing us of wasting CPU cycles. | ||
"Print results stored in `stats` to `output` using field separator `FS` and line separator `LS`." | ||
@generated function log_results(out, stats; FS="\t", LS="\n") | ||
fn = fieldnames(stats) | ||
ft = fieldtypes(stats) | ||
|
||
fn_body = Expr(:block) | ||
|
||
# all fields of stats | ||
for (i, (name, typ)) in enumerate(zip(fn, ft)) | ||
# aggregate stats | ||
if typ <: NamedTuple | ||
# go through all elements of stats.name | ||
for (j, tname) in enumerate(fieldnames(typ)) | ||
push!(fn_body.args, :(print(out, stats.$name.$tname))) | ||
if j < length(fieldnames(typ)) | ||
push!(fn_body.args, :(print(out, FS))) | ||
end | ||
end | ||
# single values | ||
else | ||
push!(fn_body.args, :(print(out, stats.$name))) | ||
end | ||
|
||
if i < length(fn) | ||
push!(fn_body.args, :(print(out, FS))) | ||
end | ||
end | ||
|
||
push!(fn_body.args, :(print(out, LS))) | ||
|
||
fn_body | ||
end | ||
|
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,50 @@ | ||
"obtain a named tuple type with the same field types and names as `struct_T`" | ||
tuple_type(struct_T) = NamedTuple{fieldnames(struct_T), Tuple{fieldtypes(struct_T)...}} | ||
|
||
"construct a named tuple from `x`" | ||
@generated function to_named_tuple(x) | ||
if x <: NamedTuple | ||
return :x | ||
end | ||
|
||
# constructor call | ||
tuptyp = Expr(:quote, tuple_type(x)) | ||
|
||
# constructor arguments | ||
tup = Expr(:tuple) | ||
for i in 1:fieldcount(x) | ||
push!(tup.args, :(getfield(x, $i)) ) | ||
end | ||
|
||
# both put together | ||
:($tuptyp($tup)) | ||
end | ||
|
||
|
||
"translate accumulator types into prefixes for the header (e.g. min, max, etc.)" | ||
stat_names(::Type{T}) where {T} = fieldnames(result_type(T)) | ||
|
||
|
||
# it would be much nicer to use a generated function for this, but | ||
# unfortunately we are already operating on types | ||
"concatenate named tuple and/or struct types into one single named tuple" | ||
function joined_named_tuple_T(types...) | ||
ns = Expr(:tuple) | ||
ts = Expr(:curly) | ||
push!(ts.args, :Tuple) | ||
|
||
for t in types | ||
fnames = fieldnames(t) | ||
ftypes = fieldtypes(t) | ||
|
||
append!(ns.args, QuoteNode.(fnames)) | ||
append!(ts.args, ftypes) | ||
end | ||
|
||
ret = :(NamedTuple{}) | ||
push!(ret.args, ns) | ||
push!(ret.args, ts) | ||
|
||
eval(ret) | ||
end | ||
|