Function st = celltravel(c,f)
st = celltravel(c,f,st)
st = celltravel(c,f,st,opt)
travels through the given possibly (recursively) nested
cell-array c, and applies the given function f "at each
level of c gradually".
Cell-array c is thought of as composed by "levels". Each
level is a one-dimensional array and its elements contain
e.g. an operator and its arguments to represent an
operation:
{"plus",3,4}
An example of a recursively nested cell-array is:
cx = {"plus",3,{"times",{"minus",4,7},5}}
where the third element has got a second level to
represent a nested operation, and this second level
contains another nesting level at its second element.
Note that we can identify each (nested) element with an
index vector and retrieve its value with function
cellget(). With the above example:
cellget(cx,[1])
returns "plus",
cellget(cx,[3 2])
returns {"minus",4,7}, and
cellget(cx,[3 2 3])
returns 7.
Function celltravel() starts travelling at the first
element in c. That is c{1}, at which the general two-stage
travelling procedure starts. The first stage is to detect,
whether the element contains a cell-array. If so, the
function descends into the lower level and detects,
whether the first element at this new (actual) level is a
cell-array. The first stage is repeated until a "literal
value" (i.e. an other value than a cell-array) is
detected. Then, this literal value is stored, and the
function switches to the second stage.
The second stage is to call function f with the values
which have been stored correspondingly to the elements
which have been detected so far at the actual level. If
all elements at the actual level have been detected,
function f may return a result value to be stored in
correspondence to the whole level. Next, function
celltravel() either goes to the next element at the actual
level and switches to the first stage, or ascends to the
element at the higher level and applies there the second
stage.
This is why we say that function f is applied "at each
level of c gradually": At first f is called with only the
value of the first element at the particular level. Later,
f is called with the values of the first two elements.
This goes on, and finally f is called with the values of
all elements of the particular level.
Function celltravel() will stumble upon the literal
values of the cell-array cx of the above example during
the first stages as follows:
{<1>,<2>,{<3>,{<4>,<5>,<6>},<7>}}
The second stages (i.e. calls to function f) will occur
with these arguments in this ordre:
{"plus"}
{"plus",3}
{"times"}
{"minus"}
{"minus",4}
{"minus",4,7} % let the result be <tmp1>
{"times",<tmp1>}
{"times",<tmp1>,5} % let the result be <tmp2>
{"plus",3,<tmp2>} % let the result be <tmp3>
The second input argument f contains a function name or
handle, or an anynomous function. It must accept struct st
as the onely input argument and the onely output argument,
thus: st = f(st).
At the detection during the first stage, function
celltravel() distinguishes the following values in an
element:
-- a cell-array in which the first element matches
exactly with the string given by opt.opname4literal. In
this case, the second element is taken as the literal
value, it is encapsulated, and function celltravel()
proceeds to the next element;
-- an other cell-array, which is not taken as a literal
value. In this case, function celltravel() descends
deeper into this cell-array;
-- a struct with a field with the name given by the
string in opt.fieldname4data. In this case, the value
of this field is regarded as the literal value, but
function celltravel() does not change the struct and
proceeds to the next element;
-- any other value. In this case, this value is taken as
the literal value, it is encapsulated, and function
celltravel() proceeds to the next element.
A value is encapsulated by putting it into a struct with
a field with the name given by the string in
opt.fieldname4data.
If input cell-array c contains "spurious" nesting, it may
be necessary to "flatten" it first, e.g. by function
cellflatten().
Input and output argument st is a struct and is used to
pass various state values to celltravel() and function f.
Struct st is organised as follows:
-- as input when function f is called:
st.(opt.fieldname4ic) : index vector of the actual
level (which is not the actual element).
st.(opt.fieldname4iel) : index of the actual element at
the actual level. The index vector of the actual element
is composed by:
[st.(opt.fieldname4ic) st.(opt.fieldname4iel)].
st.(opt.fieldname4nel) : number of elements at the
actual level.
st.(opt.fieldname4arg) : cell-array with the values
corresponding to the elements 1:iel at the actual level.
Note that every literal value has been encapsulated into
a struct with a field with the name given by the string
in opt.fieldname4data.
st.(opt.fieldname4reachedframe) : boolean to signal to
function f to leave the actual frame. It is set to true
by celltravel(), when the upper boundary of the actual
is reached. In that case, function f is directly called
for the whole level in which the element once given by
st.(opt.fieldname4jumptoelem) resides. Function f must
then reply with st.(opt.fieldname4leaveframe). This all
is e.g. useful in subroutines without an explicit
return-operation; celltravel() will set
st.(opt.fieldname4reachedframe) to true and directly
call function f for {"subroutine",<name>,<block>}, when
it is about to leave <block>. Function f will then know
that <block> is sitting in a {"subroutine", ...}, not in
some other type of construct.
-- as possible output from function f:
st.(opt.fieldname4ret) : result value of function f.
The value is stored correspondingly to the actual level
(and thus it is not stored correspondingly to the actual
element).
st.(opt.fieldname4jumptoelem) : index of the element at
the actual level to jump to. This is e.g. useful for
if-then-else-operations, like
{"ifthenelse",<A>,<B>,<C>}. To jump beyond the last
element of the actual level, set the value of
st.(opt.fieldname4jumptoelem) to a larger value than the
total number of elements in the level.
st.(opt.fieldname4enterframe) : index vector of the
element (at any level) to jump to. A new frame is
started, i.e. celltravel() memorises from where it
jumps, and the new position is now the upper boundary of
the new frame. This is e.g. useful for call-operations,
like {"call",<subroutinename>,<arglist>}.
st.(opt.fieldname4leaveframe) : empty value, to
indicate to leave the frame. So, celltravel() returns to
the previous frame and resumes at the element where it
had stopped.
-- as final output when celltravel() stops:
st.(opt.fieldname4result) : final result, put in struct
st at the end of function celltravel().
-- always existing, as possible output from function f,
and as final output when celltravel() stops:
st.(opt.fieldname4error) : if the field is not empty,
an error occurred. When this field is filled by function
f, celltravel() will stop imediately (the program is not
aborted).
Fields in st only exist, when they are necessary for to
convey a certain state and to exchange information between
the function celltravel() and function f. The exception is
st.(opt.fieldname4error), which exists without
interruption.
Other fields in st may exist at will to store information
between the subsequent calls to function f.
Struct st may be given as a third optional input argument
to celltravel().
The optional fourth argument opt is a struct and is
organised as follows:
opt.("fieldname4...") : a string to define a field name
in st; see above. See into the code of m-file
celltravel.m for the default values.
opt.opname4literal : a string ("lit" at default) for
the operator name in the cell-array {opname,data}, in
which the second element is taken as a literal value.
Such cell-arrays are nested into input cell-array c,
when a struct or cell-array has to be included as data
and not to be interpreted.
opt.fieldname4data : a string ("data" at default) for
encapsulating literal values.
opt.allerrorsinstate : a boolean (false at default),
whether the message of the error thrown by celltravel()
is put in st.(opt.fieldname4error). If it is false, the
error thrown by celltravel() will abort the program.
opt.iloopmax : an integer (inf at default) for the
maximum number of loops.
opt.debug : boolean (false at default), whether to show
what celltravel() is doing internally at every step.
Function celltravel() does not call itself recursively.
The function internally calls the following non-standard
m-files:
-- cellget.m
-- cellset.m
-- displayf.m
-- errormsg.m
(c) 2026 fabien van mook
2026.08.20 release of this file within package "fvm-celltravel" under GNU GPLv3+