TUTORIAL: Comma-Separated Lists and How to Use Them

67 views (last 30 days)
Stephen23 on 23 Feb 2022
Edited: Stephen23 on 18 May 2022
Comma-separated lists are really very simple. You use them all the time. Here is one:
That is a comma-separated list containing four variables, the variables a, b, c, and d. Every time you write a list separated by commas then you are writing a comma-separated list. Most commonly you would write a comma-separated list as inputs when calling a function or operator:
or as the function outputs:
[a,b,c,d] = fun();
It is very important to understand that in general a comma-separated list is NOT one variable (but it could be). However, sometimes it is useful to create a comma-separated list from one variable (or define one variable from a comma-separated list), and MATLAB has several ways of doing this from various container array types:
struct_array.field % all elements
struct_array(idx).field % selected elements
cell_array{:} % all elements
cell_array{idx} % selected elements
3) from a string array (Undocumented, but works fine. Note that a string array contains char vectors!):
string_array{:} % all elements
string_array{idx} % selected elements
Note that these are still exactly equivalent to what I wrote at the top: they will generate this comma-separated list:
variable1, variable2, variable3, .. , variableN
and they will generate as many separate variables in that list as the structure or cell or string array has elements (or that are selected using indexing). A comma-separated list of one is equivalent to one single variable, but in general there can be any number of separate variables in the comma-separated list (zero, one, two, three, four, or more variables). Here is an example showing that a comma-separated list generated from a cell array is the same as a comma-separated list written explicitly:
>> C = {1,0,Inf};
>> C{:}
ans =
ans =
ans =
>> 1,0,Inf
ans =
ans =
ans =
How to Use Comma-Separated Lists
Function Inputs: Remember that every time you call a function with multiple input arguments you are using a comma-separated list:
and this is exactly why they are useful: because you can specify the arguments for a function or operator without knowing anything about the arguments (even how many there are). Using the example cell array from above:
>> vertcat(C{:})
ans =
which, as we should know by now, is exactly equivalent to writing the same comma-separated list directly into the function call:
>> vertcat(1,0,Inf)
ans =
How can we use this? Commonly these are used to generate vectors of values from a structure or cell array, e.g. to concatenate the filenames which are in the output structure of dir:
S = dir(..);
N = {S.name}
which is simply equivalent to
N = {S(1).name, S(2).name, S(3).name, .. , S(end).name}
Or, consider a function with multiple optional input arguments:
opt = {'HeaderLines',2, 'Delimiter',',', 'CollectOutputs',true);
fid = fopen(...);
C = textscan(fid,'%f%f',opt{:});
Note how we can pass the optional arguments as a comma-separated list. Remember how a comma-separated list is equivalent to writing var1,var2,var3,..., then the above example is really just this:
C = textscan(fid,'%f%f', 'HeaderLines',2, 'Delimiter',',', 'CollectOutputs',true)
with the added advantage that we can specify all of the optional arguments elsewhere and handle them as one cell array (e.g. as a function input, or at the top of the file). Or we could select which options we want simply by using indexing on that cell array. Note that varargin and varargout can also be useful here.
Function Outputs: In much the same way that the input arguments can be specified, so can an arbitrary number of output arguments. This is commonly used for functions which return a variable number of output arguments, specifically ndgrid and ind2sub. For example we can easily get all outputs of ndgrid, for any number of inputs (in this example three inputs and three outputs, determined by the number of elements in the cell array):
C = {1:3,4:7,8:9};
[C{:}] = ndgrid(C{:});
which is thus equivalent to:
[C{1},C{2},C{3}] = ndgrid(C{1},C{2},C{3});
Click the links to jumpt to the "Answers" below:
Just remember that in general a comma-separated list is not one variable (although they can be), and that they are exactly what they say: a list (of variables) separated with commas. You use them all the time without even realizing it, every time you write this:

Answers (2)

Jan on 23 Feb 2022
An interesting application of comma-separated lists are dynamic indexing. You can use it to extract a specific dimension of a multi-dimensional array programatically:
X = rand(2, 3, 4, 5);
dim = 3;
tile = cell(1, ndims(X));
tile(:) = {':'};
tile{2} = 1;
slice = X(tile{:}) % As X(:, :, 1, :)
slice =
slice(:,:,1,1) = 0.9209 0.2163 slice(:,:,2,1) = 0.7218 0.9227 slice(:,:,3,1) = 0.0267 0.5008 slice(:,:,4,1) = 0.7944 0.0705 slice(:,:,1,2) = 0.0151 0.4040 slice(:,:,2,2) = 0.5073 0.7206 slice(:,:,3,2) = 0.6955 0.7741 slice(:,:,4,2) = 0.0709 0.0093 slice(:,:,1,3) = 0.3826 0.4903 slice(:,:,2,3) = 0.4400 0.6640 slice(:,:,3,3) = 0.9310 0.1062 slice(:,:,4,3) = 0.7566 0.7873 slice(:,:,1,4) = 0.2424 0.4560 slice(:,:,2,4) = 0.6969 0.9106 slice(:,:,3,4) = 0.6347 0.7058 slice(:,:,4,4) = 0.3012 0.0812 slice(:,:,1,5) = 0.8780 0.2131 slice(:,:,2,5) = 0.3748 0.6214 slice(:,:,3,5) = 0.0283 0.4716 slice(:,:,4,5) = 0.3078 0.3399

Stephen23 on 18 May 2022
Edited: Stephen23 on 18 May 2022
A common misunderstanding of comma-separated lists involves nested structures. The expectation is that dot-indexing at arbitrary levels of the nesting should somehow be converted into just one comma-separated list, regardless of the sizes of the nested structures.
However this is not the case: every dot index is essentially independent from every other dot index, resulting in independent comma-separated lists.
In practice this means that nested structures can only be chained into "one" dot-indexing operation when all parent structures are scalar (there can be zero or more scalar parent structures). For example, here A is scalar:
A.students(1).grades = [7,8,9];
A.students(2).grades = [6,7,8];
A % parent is scalar
A = struct with fields:
students: [1×2 struct]
A.students % non-scalar
ans = 1×2 struct array with fields:
[A.students.grades] % :)
ans = 1×6
7 8 9 6 7 8
whereas here B is non-scalar (so its dot-index returns mutlple separate structures, not one, so cannot be further indexed):
B(1).students.grades = [1,2,3];
B(2).students.grades = [3,2,1];
B % parent is non-scalar
B = 1×2 struct array with fields:
B.students % this returns multiple separate structures!
ans = struct with fields:
grades: [1 2 3]
ans = struct with fields:
grades: [3 2 1]
[B.students.grades] % error!
Intermediate dot '.' indexing produced a comma-separated list with 2 values, but it must produce a single value when followed by subsequent indexing operations.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!