Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
12 views

121 PDFsam Matlab Prog

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views

121 PDFsam Matlab Prog

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

Tokens in Regular Expressions

loc2 = regexp(chr2, expr, 'names')

loc2 =

struct with fields:

adrs: '26 Walnut Road'


city: 'Topeka'
state: 'KA'
zip: '25384'

loc3 = regexp(chr3, expr, 'names')

loc3 =

struct with fields:

adrs: '847 Industrial Drive'


city: 'Elizabeth'
state: 'NJ'
zip: '73548'

See Also
regexp | regexpi | regexprep

More About
• “Regular Expressions” on page 2-51

2-71
2 Program Components

Dynamic Regular Expressions

In this section...
“Introduction” on page 2-72
“Dynamic Match Expressions — (??expr)” on page 2-73
“Commands That Modify the Match Expression — (??@cmd)” on page 2-73
“Commands That Serve a Functional Purpose — (?@cmd)” on page 2-74
“Commands in Replacement Expressions — ${cmd}” on page 2-76

Introduction
In a dynamic expression, you can make the pattern that you want regexp to match dependent on the
content of the input text. In this way, you can more closely match varying input patterns in the text
being parsed. You can also use dynamic expressions in replacement terms for use with the
regexprep function. This gives you the ability to adapt the replacement text to the parsed input.

You can include any number of dynamic expressions in the match_expr or replace_expr
arguments of these commands:

regexp(text, match_expr)
regexpi(text, match_expr)
regexprep(text, match_expr, replace_expr)

As an example of a dynamic expression, the following regexprep command correctly replaces the
term internationalization with its abbreviated form, i18n. However, to use it on a different
term such as globalization, you have to use a different replacement expression:

match_expr = '(^\w)(\w*)(\w$)';

replace_expr1 = '$118$3';
regexprep('internationalization', match_expr, replace_expr1)

ans =

'i18n'

replace_expr2 = '$111$3';
regexprep('globalization', match_expr, replace_expr2)

ans =

'g11n'

Using a dynamic expression ${num2str(length($2))} enables you to base the replacement


expression on the input text so that you do not have to change the expression each time. This
example uses the dynamic replacement syntax ${cmd}.

match_expr = '(^\w)(\w*)(\w$)';
replace_expr = '$1${num2str(length($2))}$3';

regexprep('internationalization', match_expr, replace_expr)

2-72
Dynamic Regular Expressions

ans =

'i18n'

regexprep('globalization', match_expr, replace_expr)

ans =

'g11n'

When parsed, a dynamic expression must correspond to a complete, valid regular expression. In
addition, dynamic match expressions that use the backslash escape character (\) require two
backslashes: one for the initial parsing of the expression, and one for the complete match. The
parentheses that enclose dynamic expressions do not create a capturing group.

There are three forms of dynamic expressions that you can use in match expressions, and one form
for replacement expressions, as described in the following sections

Dynamic Match Expressions — (??expr)


The (??expr) operator parses expression expr, and inserts the results back into the match
expression. MATLAB then evaluates the modified match expression.

Here is an example of the type of expression that you can use with this operator:
chr = {'5XXXXX', '8XXXXXXXX', '1X'};
regexp(chr, '^(\d+)(??X{$1})$', 'match', 'once');

The purpose of this particular command is to locate a series of X characters in each of the character
vectors stored in the input cell array. Note however that the number of Xs varies in each character
vector. If the count did not vary, you could use the expression X{n} to indicate that you want to match
n of these characters. But, a constant value of n does not work in this case.

The solution used here is to capture the leading count number (e.g., the 5 in the first character vector
of the cell array) in a token, and then to use that count in a dynamic expression. The dynamic
expression in this example is (??X{$1}), where $1 is the value captured by the token \d+. The
operator {$1} makes a quantifier of that token value. Because the expression is dynamic, the same
pattern works on all three of the input vectors in the cell array. With the first input character vector,
regexp looks for five X characters; with the second, it looks for eight, and with the third, it looks for
just one:
regexp(chr, '^(\d+)(??X{$1})$', 'match', 'once')

ans =

1×3 cell array

{'5XXXXX'} {'8XXXXXXXX'} {'1X'}

Commands That Modify the Match Expression — (??@cmd)


MATLAB uses the (??@cmd) operator to include the results of a MATLAB command in the match
expression. This command must return a term that can be used within the match expression.

For example, use the dynamic expression (??@flilplr($1)) to locate a palindrome, “Never Odd or
Even”, that has been embedded into a larger character vector.

2-73
2 Program Components

First, create the input string. Make sure that all letters are lowercase, and remove all nonword
characters.

chr = lower(...
'Find the palindrome Never Odd or Even in this string');

chr = regexprep(chr, '\W*', '')

chr =

'findthepalindromeneveroddoreveninthisstring'

Locate the palindrome within the character vector using the dynamic expression:

palindrome = regexp(chr, '(.{3,}).?(??@fliplr($1))', 'match')

palindrome =

1×1 cell array

{'neveroddoreven'}

The dynamic expression reverses the order of the letters that make up the character vector, and then
attempts to match as much of the reversed-order vector as possible. This requires a dynamic
expression because the value for $1 relies on the value of the token (.{3,}).

Dynamic expressions in MATLAB have access to the currently active workspace. This means that you
can change any of the functions or variables used in a dynamic expression just by changing variables
in the workspace. Repeat the last command of the example above, but this time define the function to
be called within the expression using a function handle stored in the base workspace:

fun = @fliplr;

palindrome = regexp(chr, '(.{3,}).?(??@fun($1))', 'match')

palindrome =

1×1 cell array

{'neveroddoreven'}

Commands That Serve a Functional Purpose — (?@cmd)


The (?@cmd) operator specifies a MATLAB command that regexp or regexprep is to run while
parsing the overall match expression. Unlike the other dynamic expressions in MATLAB, this operator
does not alter the contents of the expression it is used in. Instead, you can use this functionality to
get MATLAB to report just what steps it is taking as it parses the contents of one of your regular
expressions. This functionality can be useful in diagnosing your regular expressions.

The following example parses a word for zero or more characters followed by two identical
characters followed again by zero or more characters:

regexp('mississippi', '\w*(\w)\1\w*', 'match')

ans =

1×1 cell array

2-74
Dynamic Regular Expressions

{'mississippi'}

To track the exact steps that MATLAB takes in determining the match, the example inserts a short
script (?@disp($1)) in the expression to display the characters that finally constitute the match.
Because the example uses greedy quantifiers, MATLAB attempts to match as much of the character
vector as possible. So, even though MATLAB finds a match toward the beginning of the string, it
continues to look for more matches until it arrives at the very end of the string. From there, it backs
up through the letters i then p and the next p, stopping at that point because the match is finally
satisfied:
regexp('mississippi', '\w*(\w)(?@disp($1))\1\w*', 'match')

i
p
p

ans =

1×1 cell array

{'mississippi'}

Now try the same example again, this time making the first quantifier lazy (*?). Again, MATLAB
makes the same match:
regexp('mississippi', '\w*?(\w)\1\w*', 'match')

ans =

1×1 cell array

{'mississippi'}

But by inserting a dynamic script, you can see that this time, MATLAB has matched the text quite
differently. In this case, MATLAB uses the very first match it can find, and does not even consider the
rest of the text:
regexp('mississippi', '\w*?(\w)(?@disp($1))\1\w*', 'match')

m
i
s

ans =

1×1 cell array

{'mississippi'}

To demonstrate how versatile this type of dynamic expression can be, consider the next example that
progressively assembles a cell array as MATLAB iteratively parses the input text. The (?!) operator
found at the end of the expression is actually an empty lookahead operator, and forces a failure at
each iteration. This forced failure is necessary if you want to trace the steps that MATLAB is taking to
resolve the expression.

MATLAB makes a number of passes through the input text, each time trying another combination of
letters to see if a fit better than last match can be found. On any passes in which no matches are

2-75
2 Program Components

found, the test results in an empty character vector. The dynamic script (?@if(~isempty($&)))
serves to omit the empty character vectors from the matches cell array:

matches = {};
expr = ['(Euler\s)?(Cauchy\s)?(Boole)?(?@if(~isempty($&)),' ...
'matches{end+1}=$&;end)(?!)'];

regexp('Euler Cauchy Boole', expr);

matches

matches =

1×6 cell array

{'Euler Cauchy Bo…'} {'Euler Cauchy '} {'Euler '} {'Cauchy Boole'} {'Cauchy '}

The operators $& (or the equivalent $0), $`, and $' refer to that part of the input text that is
currently a match, all characters that precede the current match, and all characters to follow the
current match, respectively. These operators are sometimes useful when working with dynamic
expressions, particularly those that employ the (?@cmd) operator.

This example parses the input text looking for the letter g. At each iteration through the text, regexp
compares the current character with g, and not finding it, advances to the next character. The
example tracks the progress of scan through the text by marking the current location being parsed
with a ^ character.

(The $` and $´ operators capture that part of the text that precedes and follows the current parsing
location. You need two single-quotation marks ($'') to express the sequence $´ when it appears
within text.)

chr = 'abcdefghij';
expr = '(?@disp(sprintf(''starting match: [%s^%s]'',$`,$'')))g';

regexp(chr, expr, 'once');

starting match: [^abcdefghij]


starting match: [a^bcdefghij]
starting match: [ab^cdefghij]
starting match: [abc^defghij]
starting match: [abcd^efghij]
starting match: [abcde^fghij]
starting match: [abcdef^ghij]

Commands in Replacement Expressions — ${cmd}


The ${cmd} operator modifies the contents of a regular expression replacement pattern, making this
pattern adaptable to parameters in the input text that might vary from one use to the next. As with
the other dynamic expressions used in MATLAB, you can include any number of these expressions
within the overall replacement expression.

In the regexprep call shown here, the replacement pattern is '${convertMe($1,$2)}'. In this
case, the entire replacement pattern is a dynamic expression:

regexprep('This highway is 125 miles long', ...


'(\d+\.?\d*)\W(\w+)', '${convertMe($1,$2)}');

2-76
Dynamic Regular Expressions

The dynamic expression tells MATLAB to execute a function named convertMe using the two tokens
(\d+\.?\d*) and (\w+), derived from the text being matched, as input arguments in the call to
convertMe. The replacement pattern requires a dynamic expression because the values of $1 and $2
are generated at runtime.

The following example defines the file named convertMe that converts measurements from imperial
units to metric.
function valout = convertMe(valin, units)
switch(units)
case 'inches'
fun = @(in)in .* 2.54;
uout = 'centimeters';
case 'miles'
fun = @(mi)mi .* 1.6093;
uout = 'kilometers';
case 'pounds'
fun = @(lb)lb .* 0.4536;
uout = 'kilograms';
case 'pints'
fun = @(pt)pt .* 0.4731;
uout = 'litres';
case 'ounces'
fun = @(oz)oz .* 28.35;
uout = 'grams';
end
val = fun(str2num(valin));
valout = [num2str(val) ' ' uout];
end

At the command line, call the convertMe function from regexprep, passing in values for the
quantity to be converted and name of the imperial unit:
regexprep('This highway is 125 miles long', ...
'(\d+\.?\d*)\W(\w+)', '${convertMe($1,$2)}')

ans =

'This highway is 201.1625 kilometers long'

regexprep('This pitcher holds 2.5 pints of water', ...


'(\d+\.?\d*)\W(\w+)', '${convertMe($1,$2)}')

ans =

'This pitcher holds 1.1828 litres of water'

regexprep('This stone weighs about 10 pounds', ...


'(\d+\.?\d*)\W(\w+)', '${convertMe($1,$2)}')

ans =

'This stone weighs about 4.536 kilograms'

As with the (??@ ) operator discussed in an earlier section, the ${ } operator has access to
variables in the currently active workspace. The following regexprep command uses the array A
defined in the base workspace:
A = magic(3)

2-77
2 Program Components

A =

8 1 6
3 5 7
4 9 2

regexprep('The columns of matrix _nam are _val', ...


{'_nam', '_val'}, ...
{'A', '${sprintf(''%d%d%d '', A)}'})

ans =

'The columns of matrix A are 834 159 672'

See Also
regexp | regexpi | regexprep

More About
• “Regular Expressions” on page 2-51

2-78
Comma-Separated Lists

Comma-Separated Lists
In this section...
“What Is a Comma-Separated List?” on page 2-79
“Generating a Comma-Separated List” on page 2-79
“Assigning Output from a Comma-Separated List” on page 2-81
“Assigning to a Comma-Separated List” on page 2-81
“How to Use the Comma-Separated Lists” on page 2-82
“Fast Fourier Transform Example” on page 2-84

What Is a Comma-Separated List?


Typing in a series of numbers separated by commas gives you what is called a comma-separated list.
The MATLAB software returns each value individually:
1,2,3

ans =

ans =

ans =

Such a list, by itself, is not very useful. But when used with large and more complex data structures
like MATLAB structures and cell arrays, the comma-separated list can enable you to simplify your
MATLAB code.

Generating a Comma-Separated List


This section describes how to generate a comma-separated list from either a cell array or a MATLAB
structure.

Generating a List from a Cell Array

Extracting multiple elements from a cell array yields a comma-separated list. Given a 4-by-6 cell array
as shown here
C = cell(4,6);
for k = 1:24
C{k} = k*2;
end
C

C =

2-79
2 Program Components

[2] [10] [18] [26] [34] [42]


[4] [12] [20] [28] [36] [44]
[6] [14] [22] [30] [38] [46]
[8] [16] [24] [32] [40] [48]

extracting the fifth column generates the following comma-separated list:

C{:,5}

ans =

34

ans =

36

ans =

38

ans =

40

This is the same as explicitly typing

C{1,5},C{2,5},C{3,5},C{4,5}

Generating a List from a Structure

For structures, extracting a field of the structure that exists across one of its dimensions yields a
comma-separated list.

Start by converting the cell array used above into a 4-by-1 MATLAB structure with six fields: f1
through f6. Read field f5 for all rows and MATLAB returns a comma-separated list:

S = cell2struct(C,{'f1','f2','f3','f4','f5','f6'},2);
S.f5

ans =

34

ans =

36

ans =

38

2-80
Comma-Separated Lists

ans =

40

This is the same as explicitly typing

S(1).f5,S(2).f5,S(3).f5,S(4).f5

Assigning Output from a Comma-Separated List


You can assign any or all consecutive elements of a comma-separated list to variables with a simple
assignment statement. Using the cell array C from the previous section, assign the first row to
variables c1 through c6:

C = cell(4,6);
for k = 1:24
C{k} = k*2;
end
[c1,c2,c3,c4,c5,c6] = C{1,1:6};
c5

c5 =

34

If you specify fewer output variables than the number of outputs returned by the expression, MATLAB
assigns the first N outputs to those N variables, and then discards any remaining outputs. In this next
example, MATLAB assigns C{1,1:3} to the variables c1, c2, and c3, and then discards C{1,4:6}:

[c1,c2,c3] = C{1,1:6};

You can assign structure outputs in the same manner:

S = cell2struct(C,{'f1','f2','f3','f4','f5','f6'},2);
[sf1,sf2,sf3] = S.f5;
sf3

sf3 =

38

You also can use the deal function for this purpose.

Assigning to a Comma-Separated List


The simplest way to assign multiple values to a comma-separated list is to use the deal function. This
function distributes all of its input arguments to the elements of a comma-separated list.

This example uses deal to overwrite each element in a comma-separated list. First create a list.

c{1} = [31 07];


c{2} = [03 78];
c{:}

ans =

31 7

2-81
2 Program Components

ans =

3 78

Use deal to overwrite each element in the list.

[c{:}] = deal([10 20],[14 12]);


c{:}

ans =

10 20

ans =

14 12

This example does the same as the one above, but with a comma-separated list of vectors in a
structure field:

s(1).field1 = [31 07];


s(2).field1 = [03 78];
s.field1

ans =

31 7

ans =

3 78

Use deal to overwrite the structure fields.

[s.field1] = deal([10 20],[14 12]);


s.field1

ans =

10 20

ans =

14 12

How to Use the Comma-Separated Lists


Common uses for comma-separated lists are

• “Constructing Arrays” on page 2-83


• “Displaying Arrays” on page 2-83

2-82
Comma-Separated Lists

• “Concatenation” on page 2-83


• “Function Call Arguments” on page 2-84
• “Function Return Values” on page 2-84

The following sections provide examples of using comma-separated lists with cell arrays. Each of
these examples applies to MATLAB structures as well.

Constructing Arrays

You can use a comma-separated list to enter a series of elements when constructing a matrix or array.
Note what happens when you insert a list of elements as opposed to adding the cell itself.

When you specify a list of elements with C{:, 5}, MATLAB inserts the four individual elements:
A = {'Hello',C{:,5},magic(4)}

A =

'Hello' [34] [36] [38] [40] [4x4 double]

When you specify the C cell itself, MATLAB inserts the entire cell array:
A = {'Hello',C,magic(4)}

A =

'Hello' {4x6 cell} [4x4 double]

Displaying Arrays

Use a list to display all or part of a structure or cell array:


A{:}

ans =

Hello

ans =

[2] [10] [18] [26] [34] [42]


[4] [12] [20] [28] [36] [44]
[6] [14] [22] [30] [38] [46]
[8] [16] [24] [32] [40] [48]

ans =

16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1

Concatenation

Putting a comma-separated list inside square brackets extracts the specified elements from the list
and concatenates them:

2-83
2 Program Components

A = [C{:,5:6}]

A =

34 36 38 40 42 44 46 48

Function Call Arguments

When writing the code for a function call, you enter the input arguments as a list with each argument
separated by a comma. If you have these arguments stored in a structure or cell array, then you can
generate all or part of the argument list from the structure or cell array instead. This can be
especially useful when passing in variable numbers of arguments.

This example passes several attribute-value arguments to the plot function:

X = -pi:pi/10:pi;
Y = tan(sin(X)) - sin(tan(X));
C = cell(2,3);
C{1,1} = 'LineWidth';
C{2,1} = 2;
C{1,2} = 'MarkerEdgeColor';
C{2,2} = 'k';
C{1,3} = 'MarkerFaceColor';
C{2,3} = 'g';
figure
plot(X,Y,'--rs',C{:})

Function Return Values

MATLAB functions can also return more than one value to the caller. These values are returned in a
list with each value separated by a comma. Instead of listing each return value, you can use a comma-
separated list with a structure or cell array. This becomes more useful for those functions that have
variable numbers of return values.

This example returns three values to a cell array:

C = cell(1,3);
[C{:}] = fileparts('work/mytests/strArrays.mat')

C =

'work/mytests' 'strArrays' '.mat'

Fast Fourier Transform Example


The fftshift function swaps the left and right halves of each dimension of an array. For a simple
vector such as [0 2 4 6 8 10] the output would be [6 8 10 0 2 4]. For a multidimensional
array, fftshift performs this swap along each dimension.

fftshift uses vectors of indices to perform the swap. For the vector shown above, the index [1 2
3 4 5 6] is rearranged to form a new index [4 5 6 1 2 3]. The function then uses this index
vector to reposition the elements. For a multidimensional array, fftshift must construct an index
vector for each dimension. A comma-separated list makes this task much simpler.

Here is the fftshift function:

2-84
Comma-Separated Lists

function y = fftshift(x)
numDims = ndims(x);
idx = cell(1,numDims);
for k = 1:numDims
m = size(x,k);
p = ceil(m/2);
idx{k} = [p+1:m 1:p];
end
y = x(idx{:});
end

The function stores the index vectors in cell array idx. Building this cell array is relatively simple.
For each of the N dimensions, determine the size of that dimension and find the integer index nearest
the midpoint. Then, construct a vector that swaps the two halves of that dimension.

By using a cell array to store the index vectors and a comma-separated list for the indexing operation,
fftshift shifts arrays of any dimension using just a single operation: y = x(idx{:}). If you were
to use explicit indexing, you would need to write one if statement for each dimension you want the
function to handle:

if ndims(x) == 1
y = x(index1);
else if ndims(x) == 2
y = x(index1,index2);
end
end

Another way to handle this without a comma-separated list would be to loop over each dimension,
converting one dimension at a time and moving data each time. With a comma-separated list, you
move the data just once. A comma-separated list makes it very easy to generalize the swapping
operation to an arbitrary number of dimensions.

2-85
2 Program Components

Alternatives to the eval Function


In this section...
“Why Avoid the eval Function?” on page 2-86
“Variables with Sequential Names” on page 2-86
“Files with Sequential Names” on page 2-87
“Function Names in Variables” on page 2-87
“Field Names in Variables” on page 2-88
“Error Handling” on page 2-88

Why Avoid the eval Function?


Although the eval function is very powerful and flexible, it is not always the best solution to a
programming problem. Code that calls eval is often less efficient and more difficult to read and
debug than code that uses other functions or language constructs. For example:

• MATLAB compiles code the first time you run it to enhance performance for future runs. However,
because code in an eval statement can change at run time, it is not compiled.
• Code within an eval statement can unexpectedly create or assign to a variable already in the
current workspace, overwriting existing data.
• Concatenated character vectors within an eval statement are often difficult to read. Other
language constructs can simplify the syntax in your code.

For many common uses of eval, there are preferred alternate approaches, as shown in the following
examples.

Variables with Sequential Names


A frequent use of the eval function is to create sets of variables such as A1, A2, ..., An, but this
approach does not use the array processing power of MATLAB and is not recommended. The
preferred method is to store related data in a single array. If the data sets are of different types or
sizes, use a structure or cell array.

For example, create a cell array that contains 10 elements, where each element is a numeric array:

numArrays = 10;
A = cell(numArrays,1);
for n = 1:numArrays
A{n} = magic(n);
end

Access the data in the cell array by indexing with curly braces. For example, display the fifth element
of A:

A{5}

ans =
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22

2-86
Alternatives to the eval Function

10 12 19 21 3
11 18 25 2 9

The assignment statement A{n} = magic(n) is more elegant and efficient than this call to eval:
eval(['A', int2str(n),' = magic(n)']) % Not recommended

For more information, see:

• “Create Cell Array” on page 12-3


• “Structure Arrays” on page 11-2

Files with Sequential Names


Related data files often have a common root name with an integer index, such as myfile1.mat
through myfileN.mat. A common (but not recommended) use of the eval function is to construct
and pass each file name to a function using command syntax, such as
eval(['save myfile',int2str(n),'.mat']) % Not recommended

The best practice is to use function syntax, which allows you to pass variables as inputs. For example:
currentFile = 'myfile1.mat';
save(currentFile)

You can construct file names within a loop using the sprintf function (which is usually more
efficient than int2str), and then call the save function without eval. This code creates 10 files in
the current folder:
numFiles = 10;
for n = 1:numFiles
randomData = rand(n);
currentFile = sprintf('myfile%d.mat',n);
save(currentFile,'randomData')
end

For more information, see:

• “Choose Command Syntax or Function Syntax” on page 1-6


• “Import or Export a Sequence of Files”

Function Names in Variables


A common use of eval is to execute a function when the name of the function is in a variable
character vector. There are two ways to evaluate functions from variables that are more efficient than
using eval:

• Create function handles with the @ symbol or with the str2func function. For example, run a
function from a list stored in a cell array:
examples = {@odedemo,@sunspots,@fitdemo};
n = input('Select an example (1, 2, or 3): ');
examples{n}()
• Use the feval function. For example, call a plot function (such as plot, bar, or pie) with data
that you specify at run time:

2-87
2 Program Components

plotFunction = input('Specify a plotting function: ','s');


data = input('Enter data to plot: ');
feval(plotFunction,data)

Field Names in Variables


Access data in a structure with a variable field name by enclosing the expression for the field in
parentheses. For example:

myData.height = [67, 72, 58];


myData.weight = [140, 205, 90];

fieldName = input('Select data (height or weight): ','s');


dataToUse = myData.(fieldName);

If you enter weight at the input prompt, then you can find the minimum weight value with the
following command.

min(dataToUse)

ans =
90

For an additional example, see “Generate Field Names from Variables” on page 11-10.

Error Handling
The preferred method for error handling in MATLAB is to use a try, catch statement. For example:

try
B = A;
catch exception
disp('A is undefined')
end

If your workspace does not contain variable A, then this code returns:

A is undefined

Previous versions of the documentation for the eval function include the syntax
eval(expression,catch_expr). If evaluating the expression input returns an error, then eval
evaluates catch_expr. However, an explicit try/catch is significantly clearer than an implicit
catch in an eval statement. Using the implicit catch is not recommended.

2-88
Classes (Data Types)

89

You might also like