-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmexximpVisitStructFields.m
93 lines (84 loc) · 3.79 KB
/
mexximpVisitStructFields.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
function struct = mexximpVisitStructFields(struct, visitFunction, varargin)
%% Recursively visit and/or update the fields in (nested) struct array.
%
% struct = mexximpVisitStructFields(struct, visitFunction, varargin)
% recursively traverses fields of the given struct array and applies the
% given visitFunction to each one. Depending on the values returned from
% the visitFunction, may update the struct field.
%
% The idea here is to recursively traverse all the fields of a nested
% struct array. And we want to write the fussy recursive code once, here
% in this function. Then we want to reuse this traversal for various
% different computations, each implemented in a different visitFunction.
%
% visitFunction must have the following inputs and outputs:
%
% funciton [value, isUpdate] = myVisitFun(value, varargin)
%
% The input value is the value of field from the original struct. Varargin
% is any extra arguments passed to this function, to pass on to the given
% visitFunction. The output value may be the same input value, or a newly
% computed value. The output isUpdate is a flag used to decide whether the
% original struct field should be updated, or just left alone.
%
% mexximpVisitStructFields( ... 'visitArgs', visitArgs) specifies a cell array of
% arguments to pass to the given visitFunction, following the current field
% value.
%
% mexximpVisitStructFields( ... 'filterFunction', filterFunction) specifies a
% function handle to use for filtering struct fields. If the
% filterFunction is provided, it will be invoked for each struct field
% value, and the given visitFunction will only be invoked if the
% filterFunction returns true. This is an optimization to avoid unnecesary
% invokations of the visitFunction.
%
% mexximpVisitStructFields( ... 'ignoreFields', ignoreFields) specifies a
% cell array of field names to ignore. Visiting will skip any fields that
% match any of these names.
%
% struct = mexximpVisitStructFields(struct, visitFunction, varargin)
%
% Copyright (c) 2016 mexximp Team
parser = inputParser();
parser.addRequired('struct', @isstruct);
parser.addRequired('visitFunction', @(f) isa(f, 'function_handle'));
parser.addParameter('visitArgs', {}, @iscell);
parser.addParameter('filterFunction', []);
parser.addParameter('ignoreFields', {}, @iscellstr);
parser.parse(struct, visitFunction, varargin{:});
struct = parser.Results.struct;
visitFunction = parser.Results.visitFunction;
visitArgs = parser.Results.visitArgs;
filterFunction = parser.Results.filterFunction;
ignoreFields = parser.Results.ignoreFields;
% kick off recursion with the original struct
struct = traverseFields(struct, visitFunction, filterFunction, ignoreFields, visitArgs);
%% Recursively traverse struct fields.
function struct = traverseFields(struct, visitFunction, filterFunction, ignoreFields, visitArgs)
nElements = numel(struct);
topLevelFields = fieldnames(struct);
for ee = 1:nElements
for tt = 1:numel(topLevelFields)
field = topLevelFields{tt};
% truncate the traversal?
if any(strcmp(ignoreFields, field))
continue;
end
value = struct(ee).(field);
if isstruct(value)
% recursive case: dig into nested struct
struct(ee).(field) = traverseFields(value, visitFunction, filterFunction, ignoreFields, visitArgs);
elseif isempty(filterFunction) || feval(filterFunction, value)
% base case: apply the visit function to the field value
try
[value, isUpdate] = feval(visitFunction, value, visitArgs{:});
catch err
fprintf('Error applying visitFunction: %s\n', err.message)
isUpdate = false;
end
if isUpdate
struct(ee).(field) = value;
end
end
end
end