2013-06-26 2 views
5

Когда MATLAB просматривает случаи в блоке switch/case, запоминает ли он значения, которые он пропускает, и можно ли получить доступ к этому списку? У меня есть несколько функций с длинным блоком switch\case, и я бы хотел, чтобы они возвращали список допустимых значений аргументов, если они сводят его до otherwise. Например, у меня есть функция, которая возвращает набор оптических констант для материала. В настоящее время он имеет около 20 различных материалов, и он растет, поскольку я считаю новые.Программно получить действительные значения switch/case

Я понимаю, что могу перетащить его и просто перепечатать все допустимые случаи в массив ячеек под otherwise и вызвать функцию, выкинув ошибку и вернуть список допустимых ответов, но поддерживая оба списка без ошибок или ленивость ползучая в течение долгого времени является сложной задачей.

+1

Вы можете найти [это] (http://www.mathworks.com/matlabcentral/fileexchange/44203-getcases-list-of-all-cases-in-a-switc h) интересно :) –

+0

Очень приятно! Я вижу, что вы в конечном итоге пошли с версией метода ужаса @ mohsennosratina :). Один комментарий. Исправьте меня, если я ошибаюсь, но этот код не сработает, если вы выделите раздел кода и нажмите F9 или выполните раздел кода с помощью Shift + Enter, если вы не сохранили файл до этого. – craigim

+0

Это действительно так; он должен быть запущен из файла. Я не мог понять, как это сделать иначе, не устанавливая требования к тому, как вы должны кодировать «переключатель» ... Возможно, когда-нибудь я знаю, как избавиться от этой зависимости :) Когда я это сделаю, я позволю вам знать. –

ответ

3

EDIT: После получения очаровательных комментариев на моем первое решение, я предлагаю другой, который нуждается в большем количестве редактирования в вашем коде, чем первый, но все же меньше, чем другие решения до сих пор (переместила оригинал до конца):

Давайте определим функцию, которая захватывает значения и сохраняет их в постоянной переменной

function list = cc(value) 
persistent allCases 
if isempty(allCases) || (nargin == 0 && nargout == 0) 
    allCases = {}; 
end 
if nargin == 1, 
    allCases = [allCases value]; 
    list = value; 
end 
if nargin == 0 && nargout == 1, 
    list = allCases; 
end 
end 

Теперь вы можете просто добавить cc; перед тем switch сбросить постоянную переменную и передать все значения в case стат ements к функции и вызовите функцию в otherwise частях, чтобы прочитать значение:

a = 'a'; 
v = 'c'; 

cc; 
switch a 
    case cc({'b' v 1.2}) 
     %Multiple cases 
    case cc(2) 
     %number 
    case cc(ones(2)) 
     %matrix 
    otherwise 
     disp('Allowed cases are:'); 
     cellfun(@disp, cc); 
end 

Это печатает:

Allowed cases are: 
b 
c 
    1.2000 
    2 
    1  1 
    1  1 

Рискованное решение: Это решение, вероятно, нарушает довольно много методов программирования, но все еще работает как хак. Предполагая, что вы не гнездились switch заявления, то вы можете вызвать функцию, как это в otherwise заявлении:

function allCases = getCases 
st = dbstack('-completenames'); 
line = st(2).line; 
fLines = importdata(st(2).file, sprintf('\n')); 
switchLine = find(~cellfun(@isempty, ... 
    regexp(fLines(1:line-1), '^\s*switch\s', 'once')), 1, 'last'); 
otherwLine = find(~cellfun(@isempty, ... 
    regexp(fLines(1:line-1), '^\s*otherwise\s*$', 'once')), 1, 'last'); 
caseLines = fLines(switchLine+1:otherwLine-1); 
casesStr = regexprep(caseLines(~cellfun(@isempty, ... 
    regexp(caseLines, '^\s*case\s', 'once'))), '^\s*case\s*', ''); 
casesCells = cell(size(casesStr)); 
for iCases = 1:numel(casesCells); 
    casesCells{iCases} = evalin('caller', casesStr{iCases}); 
end 
allCases = [casesCells{:}]; 
end 

Затем, если вы запустите код, как этот

a = 'a'; 
v = 'c'; 

switch a 
    case {'b' v 1.2} 
     %Multiple cases 
    case 2 
     %number 
    case ones(2) 
     %matrix 
    otherwise 
     disp('Allowed cases are:'); 
     cellfun(@disp, getCases); 
end 

он печатает

Allowed cases are: 
b 
c 
    1.2000 
    2 
    1  1 
    1  1 
+1

Это ужасающе. Но на самом деле решает неразрешимую проблему. Я так взволнован. – Pursuit

+0

Именно поэтому я понимаю, что ваша функция импортирует вызывающую функцию в виде текста, ищет строки со словами 'switch' и' other', а затем выполняет поиск между этими строками для всех экземпляров слова 'case', захватывает и оценивает остальную часть линии, а затем вставьте ее в ячейку? Я еще не знаю, готов ли я идти по этой дороге, пока не попробую другие варианты, но +1 для высокомерия. – craigim

+1

+1: Это ужасно! ужасно! отвратительно! возмутительно! страшно! абсолютно бесполезный, нечитаемый, неуправляемый, ... но потрясающий хак :) –

6

Чтобы уточнить, это звучит как то, что вы просите, чтобы сделать что-то вроде следующего:

value = 'z'; 
output = []; 
switch value 
    case 'a' 
     output = 1.234; 
    case 'b' 
     output = 2.345; 
    case 'c' 
     output = 3.456; 
    otherwise 
     output = []; 
     disp('Please use one the the following values: a, b, c') 
     %It would be nice to auto-populate that string wouldn't it? 
end 

Это не непосредственно возможно в Matlab (или любой другой язык, я знаю).

Однако, если вы перейдете от оператора switch/case к более ориентированному на данные коду, это упростится. Например, приведенный выше код может быть переписан как:

%Setup (this can be preloaded and stored as persistent if too time consuming) 
count = 1; 
allvalues(count).name = 'a'; allvalues(count).value = 1.234; count = count+1; 
allvalues(count).name = 'b'; allvalues(count).value = 2.345; count = count+1; 
allvalues(count).name = 'c'; allvalues(count).value = 3.456; count = count+1; 

%Lookup 
value = 'z'; %Also try value = 'a' 

maskMatch = strcmp({allvalues.name},value); 
if any(maskMatch) 
    output = allvalues(maskMatch).value; 
else 
    disp('Please use one of the following values:'); 
    disp({allvalues.name}); 
end 

Это является примером использования массива-о-структур для хранения данных. Существует множество способов использования структур данных Matlab для хранения таких данных, например. Карта или массив ячеек. Для получения более полного списка см. Ответы на этот вопрос: MATLAB Changing the name of a matrix with each iteration

+0

Это возможно только в конкретных случаях: что, если один «случай» является строкой, а следующий - «function_handle», следующий - «логический» и т. Д.? –

+0

Затем я заменил поле '.value' полем' .functionHandle'. Затем напишите функцию (или анонимную, если она достаточно короткая) для каждого случая. Вместо 'output = data (mask) .value;', используйте 'fn = data (mask) .functionHandle; output = fn(); '. – Pursuit

+0

Я говорил о содержимом 'allvalues.name'. Вы используете 'strcmp' для векторной проверки данной строки (' значение') для всех поддерживаемых строк ('allvalues.name'). Это конкретный сценарий. Что, если вместо 'value = 'z';', содержимое 'value' может быть * любым произвольным классом *? Этот метод потеряет любую элегантность, которую он когда-то имел, особенно по сравнению с «переключателем». Общность такого монстра не * перевешивает бремя управления двумя идентичными списками ... ИМХО. –

2

AFAIK, такого механизма нет. В отдельных случаях может быть возможно сняться с некоторыми тэгами векторизации, но обычно говоря, нет.

И, если только с точки зрения эффективности (память), также было бы неплохо реализовать switch подобные (все случаи может быть огромными матрицами).

Вы можете взломать вместе такой механизм:

% define all your cases in a cell 
cases = {... 
    'case1', 'case2', ...}; 

% and switch on these cases 
switch [condition] 
    case cases{1} 
     % implement 'case1' 

    case cases{2} 
     % implement 'case2' 

    ... 

    otherwise 
     char(cases) % contains all cases 

end 

Очевидно, что вы получить в общности, вы потерять читабельность; индивидуальные случаи теперь не в том же месте, что и их соответствующий код. Плюс ко всему, порядок содержания cases имеет важное значение, и [продолжает перечислять много недостатков] ...

Короче говоря, это не очень красиво.

Вы можете создать список самостоятельно на лету:

cases = {}; 

% FIRST CASE 
if strcmp([condition], 'case1') 
    % code for 'case1' 

else % insert the case just checked for in the new list 
    cases{end+1} = 'case1'; 
end 

% SECOND CASE 
if strcmp([condition], 'case2') 
    % code for 'case2' 

else % insert the case just checked for in the new list 
    cases{end+1} = 'case2'; 
end 

... % etc. 

Вы по-прежнему в основном строит 2 списка: один «анонимный» список в if -statements, и один в массиве cases клеток , Но каждый «случай» по-прежнему является индивидуальной сущностью, а условия случая сгруппированы по каждому случаю.

Конечно, вы do теряете мощность switch.

... и есть еще много схем. Все сопоставимы, и все с сопоставимыми недостатками.

Я боюсь, что лучший способ пойти сюда - просто жить с ним и управлять двумя идентичными списками.