Some of Matlab's toolbox functions are affected by magic strings or magic numbers, which are strings or numbers with a deeper meaning besides the normal value. Both are considered as bad programming patters, because they provoke confusions, when the magic keys appear with the normal meaning by accident. See http://en.wikipedia.org/wiki/Anti-pattern
Example 1:
clear('myVariable')
clear('variables')
While the 1st clears the variable myVariable, the later clears all variables. Here 'variables' has a meta-meaning. The problem appears, when 'variables' is an existing variable:
a = 1;
variables = 2;
clear('variables')
disp(a) % >> 1
Only variables is cleared, which cannot be understood directly when its definition is 1000 lines before.
Example 2:
uicontrol('String', 'default')
This creates a button with the empty string '' instead of the expected 'default', because this is the magic string to invoke the default value get(0, 'DefaultUIControlString'). The same concerns properties of other graphic objects also, e.g. the 'name' property of figure or the string of uimenu. There is a workaround which allows the user to display 'default': Simply use '\default'. Unfortunately this is doubled magic, because in consequence it is impossible to display the string '\default'. Obviously a bad idea.
Example 3:
Graphic handles are doubles (although gobject of the new R2013a seems, like this is subject to changes? [EDITED: Yes, it changed with HG2 in R2014a]). But then a handle can be confused with data:
a = axes; % e.g. 0.0048828125
plot(a, 2, '+')
But you cannot draw the point [0.0048828125, 2] by this way, because the 1st input is considered as handle of the parent. Here all possible values of handles are magic. Collisions are very unlikely, but there is no way to avoid them reliably - as long as handles have the type double.
Question:
Which functions are concerned by magic values? What are the pitfalls and workarounds?
21 Comments
In sprintf and fprintf the backslash is magic in the format strings, because it is used to make the special characters magic, e.g. '\n' becomes a line break caracter char(10). If you want the backslash to appear, '\\' is required.
This well known escaping works well in many programming languages. The workaround to use \\ to display one \ appears seldom in the forums. But sometimes users did not consider this when the format string is provided as variable:
This works fine:
S = 'hello'
fprintf([S, '\n']);
This fails under Windows due to the backslashes in the path:
S = which('plot.m');
fprintf([S, '\n']);
The general solution is not to insert dynamically created strings in athe format string, but to let %s handle the escape characters:
fprintf('%s\n', S);
Equivalent effects appear in error messages:
error(['Cannot open: ', FileName]) % Prone to bad output
error('Cannot open: %s', FileName)
[EDITED] The problem of escape characters in the error message appeared in several 3rd party Matlab toolboxes, which have been developed under Linux. Then porting them to Windows requires to consider the backslashes in the path names.
When legend is used in the form legend('string'), the following names have magic side-effects:
'off', 'toggle', 'hide', 'show', 'boxoff', 'boxon'
A workaround is either to provide the line handle as first input or to use a cellstring to store the names:
h = line(rand(1,10));
legend(h, 'off') % 'off' appears as name
legend({'hide'}); % 'hide' appears as name
Although the documentation is clear and there is no magic meta meaning of a specific string, something is magic with the save command. There is a surprising large number of problems in the Matlab forums concerning the confusion of:
save filename A
save 'filename' A
save(filename, A)
save('filename', 'A')
And in the next step for the import:
load(filename)
Data = load(filename)
A = Data.A
A = load(filename, 'A')
But what could be the reason for the frequent problems? save is one of the very rare Matlab functions, which do not get the inputs as value, but as name of the variable. While this is considered as a bad programming style for eval and assignin, it is the standard for save. Modern Matlab versions accept the '-struct' flag (well done, TMW! Due to the leading '-' this is not a magic string), such that a kind of passing by value happens.
The output of load must be a struct, but when only one variable is saved, this feels like an unexpected level of abstraction.
Nevertheless, no magic here. The documentation is complete. So this remains a job for the forum.
close name % Close figure with the specified name
close all % Close all non hidden figures
Then 'all' is magic: Even when a figure is called 'all', close('all') closes all figures and not only the one with the matching name. This differs from the behavior of clear all, which clears the variable called all only, if it is existing.
This seems to become a one-man tutorial thread as in the early days of this forum.
Matlab strings do not use a terminator to mark the end, but the C-routines, which are called for IO operations consider char(0) as terminator:
s = char([65, 0, 66])
% >> A B
disp(s)
% >> A B
fprintf('%s\n', s)
% >> A (in 2009a)
% >> AB (in 2011b)
This is not pure magic, but a in fprintf the \0 has an additional meta meaning compared to disp.
I've found a magic character in strings for the GUIs:
uicontrol('Style', 'listbox', 'Pos', [1,1,100,100], 'String', 'asd|bsd')
Then the bar is used as separator. This does not matter the style 'edit' or 'text', but the same method can be used to separate tick marks:
set(gca, ...
'XLim', [0, 40], ...
'XTick', 0:5:40, ...
'XTickLabel', '0| |10| |20| |30| |40')
[EDITED] To display bars, use a cell string instead. (Thanks, Walter)
uicontrol's, figure's and uimenu's use these magic strings for properties, which are strings:
'default', 'remove', 'factory'
Such properties are e.g. 'String', 'Callback', 'Name', 'Tag', etc.
A bullet-proof workaround:
S = input('Input the button string: ', 's');
if any(strcmp(S, {'default', 'remove', 'factory'})
S = ['\', S];
elseif any(strcmp(S, {'\default', '\remove', '\factory'})
warning('String "%s" padded to support uicontrol!', S);
S = [' ', S];
end
uicontrol('String', S);
Such an ugly catching of rare exceptions is a typical effect of magic strings.
When a callback function is called 'default', use the function handle instead of a string:
uicontrol('Callback', @default);
A real solution, not only a workaround, is to strictly avoid using these names as function names or as 'String' property and never allow not controllable user-input as a property. But this can be a limitation, if e.g. the ten most frequent words of an arbitrary text file should be displayed in 10 buttons.
@jan--you think such ruminations are worth of answer? I really hadn't considered it to quite that level...
But, I'll await your judgment and clean up as you think fit...
I will note after thinking about 1) a little more -- it's a case that could be considered
a) A bug--if a variable named 'variables' exists and the argument to clear contains 'variables' then clear also should test for that case and call itself recursively(*) (actually twice should do it); or
b) Programmer's error -- shouldn't name a variable variables. The problem there is that as above there is no definitive grammar table that gives the rules and/or the, as you call them, "magic" strings or numbers. At least in a defined language such as C or Fortran, while there are ample opportunities for programmer to make errors that aren't necessarily catchable by a compiler until runtime or by simply malfunctioning code per intent, there is a complete set of rules by which one can determine whether the code in question is or is not conforming. If, by chance, one finds a case where that isn't so, then there is a defined process by which that "hole" or anomaly or error in the Standard can be addressed and a definitive ruling made on it. AFAIK, there is no equivalent in Matlab to the defined process for the user--TMW does what TMW decides to do.
(*) Of course, this still has the problem then that there would be no way to only delete the variable variables. The root cause comes back again to the issue that one can alias anything (almost) in Matlab and that there is no rule against such name clashes enforceable except by programmer knowledge which gets more and more problematical the more features that are added.
@dpb: Please post this in the answer section.
Workarounds--
1)
clearvars % doesn't have the keyword 'variables'
Of course, since one can alias almost anything in Matlab other than a very few reserved words, one still can find ways to break it I'm sure.
2. Have to think on this one...hadn't ever come across that particular symptom in large part I suppose because I don't do gui's.
3. Always use handle first form(+), then the data that may have same value as a handle won't be referenced in a context that matters.
(+) Of course, may mean creating the empty axes() object first, then writing to the handle which is a pita to always do. Alternatively, test for a handle first and branch--equally painful to code.
All in all, these and other warts are a remnant of the more or less ad hoc design of Matlab that has simply grown from Cleve's general concept. Much like Fortran, TMW has worked to maintain as much compatibility as reasonably feasible including no typing, automagic allocation and few reserved words (Fortran has none). The effect as more and more features are added is the inevitable unless there is generated an actual formal grammar that will provide the basis for eliminating such. But, I doubt that can be done at this point w/o what are/would be considered unacceptable changes to the language.
Interesting topic, however, and at moment only the more-or-less superficial specifics of the examples is all I have time for. I'll try to think some in more depth....