Есть ли способ найти всех детей класса Matlab?

Функция Matlabsuperclasses возвращает имена всех родителей данного класса.

Есть ли эквивалент, чтобы найти все классыполученный из данного класса,то есть детские классы? Функцияallchild кажется, ограничен графическими ручками.

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

Давайте ограничимся классами в пути Matlab.

 Dev-iL28 июн. 2016 г., 11:36
@Ratbert - В моем ответе есть что-то неудовлетворительное ...?
 Bernhard21 июн. 2016 г., 14:16
Мне просто интересно, для чего вы хотите использовать это?
 Ratbert21 июн. 2016 г., 17:02
@Bernhard Я создаю движок документации для пользовательских программ. Я считаю, что было бы очень полезно иметь на странице класса список классов родителей и детей.
 Bernhard21 июн. 2016 г., 22:06
Сначала вы искали существующие решения? Если я ищу doxygen и matlab, я найду довольно много решений, которые могут сработать. Те, которые не работают для вас.
 Oleg28 июн. 2016 г., 13:02
@ Ратберт, да, было бы неплохо получить отзывы :)
 Ratbert28 июн. 2016 г., 15:21
Спасибо за ваши ответы. Они очень полны, и требуется время, чтобы проверить и понять детали. В конце концов, ответ, по сути, на то, чего я боялся: сканирование методом грубой силы - единственная стратегия ...

Ответы на вопрос(3)

Код

Я переместил код, так как он был> 200 строк в хранилище GithubgetSubclasses, Приглашаем вас ознакомиться с функциями и опубликовать отчеты об ошибках.

идея

Учитываякорень имя класса илиmeta.class и путь к папке, он будет проходить по структуре папок и строить график со всеми подклассами, полученными из корня (на бесконечной глубине). Если путь не указан, он будет найден из папки, в которой расположен корневой класс.

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

пример

Перечислите все подклассыsde учебный класс. Вам понадобится R2015b, чтобы иметь возможность создавать график, или вы можете использовать вывод и представление FEXplot_graph() создать граф зависимостей.

getSubclasses('sde','C:\Program Files\MATLAB\R2016a\toolbox\finance\finsupport')

И вывод с ребрами и именами узлов:

 names      from    to
________    ____    __
'sde'        1      1 
'bm'         2      3 
'sdeld'      3      6 
'cev'        4      3 
'gbm'        5      4 
'sdeddo'     6      1 
'heston'     7      6 
'cir'        8      9 
'sdemrd'     9      6 
'hwv'       10      9 

Мы можем сравнить результат с официальной документацией, которая в этом случае перечисляетИерархия SDEт.е.

тайминг

На Win7 64b R2016a

меньше, чем0,1 секунды: getSubclasses('sde','C:\Program Files\MATLAB\R2016a\toolbox\finance\finsupport')около13 секунд если сканировать весь matlabroot:getSubclasses('sde',matlabroot);

Не полное решение, но вы можете разобрать все.m файлы в пути в виде текста, и использовать регулярные выражения для поиска определений подкласса.

Что-то вроде^\s*classdef\s*(\w*)\s*<\s*superClassName\s*(%.*)?

Обратите внимание, что этобудут тихо завершать работу с любыми определениями подклассов, которые используют что-либо необычное, такое какeval.

 Emilio M Bumachar20 июн. 2016 г., 16:42
И это потребовало бы более сложного регулярного выражения для соответствия множественному наследованию.
 Ratbert20 июн. 2016 г., 16:41
Это также не удастся для.p файлы.
Вступление:

В ходе решения я, кажется, нашел недокументированный статический методmeta.class класс, который возвращает все кэшированные классы (почти все, что стирается, когда кто-то вызываетclear classes) а также (совершенно случайно) сделал инструмент, который проверяетclassdef файлы для ошибок.

Так как мы хотим найтивсе подклассы, верный путь, составив списоквсем известно классы, а затем проверяя для каждого из них, если он является производным от любого другого. Чтобы достичь этого, мы разделяем наши усилия на 2 типа классов:

"Массовые занятия«- здесь мы используемwhat функция для создания списка файлов, которые просто "лежат" по пути MATLAB, который выводит структуруs (описано в документахwhat имея следующие поля:'path' 'm' 'mlapp' 'mat' 'mex' 'mdl' 'slx' 'p' 'classes' 'packages', Затем мы выберем некоторые из них, чтобы составить список классов. Чтобы определить, какое содержимое имеет файл .m или .p (чтомы заботиться о том, класс / не класс), мы используемexist. Этот метод продемонстрирован Лорен в ее блоге, В моем коде этоmb_list."Пакетные занятия"- это включает в себя файлы классов, которые индексируются MATLAB как часть его внутренней структуры пакета. Алгоритм, используемый для получения этого списка, включает в себя вызовmeta.package.getAllPackages и затем рекурсивно обходит этот список пакетов верхнего уровня, чтобы получить все подпакеты. Затем из каждого пакета извлекается список классов, и все списки объединяются в один длинный список -mp_list.

Скрипт имеет два входных флага (includeBulkFiles,includePackages), которые определяют, должен ли каждый тип классов быть включен в список вывода.

Полный код ниже:

function [mc_list,subcls_list] = q37829489(includeBulkFiles,includePackages)
%% Input handling
if nargin < 2 || isempty(includePackages)
  includePackages = false;
  mp_list = meta.package.empty;
end
if nargin < 1 || isempty(includeBulkFiles)
  includeBulkFiles = false;
  mb_list = meta.class.empty; %#ok
  % `mb_list` is always overwritten by the output of meta.class.getAllClasses; 
end
%% Output checking
if nargout < 2
  warning('Second output not assigned!');
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Get classes list from bulk files "laying around" the MATLAB path:
if includeBulkFiles
  % Obtain MATLAB path:
  p = strsplit(path,pathsep).';
  if ~ismember(pwd,p)
    p = [pwd;p];
  end
  nPaths = numel(p);
  s = what; s = repmat(s,nPaths+20,1); % Preallocation; +20 is to accomodate rare cases 
  s_pos = 1;                           %  where "what" returns a 2x1 struct.
  for ind1 = 1:nPaths  
    tmp = what(p{ind1});
    s(s_pos:s_pos+numel(tmp)-1) = tmp;
    s_pos = s_pos + numel(tmp);
  end
  s(s_pos:end) = []; % truncation of placeholder entries.
  clear p nPaths s_pos tmp
  %% Generate a list of classes:
  % from .m files:
  m_files = vertcat(s.m);
  % from .p files:
  p_files = vertcat(s.p);
  % get a list of potential class names:
  [~,name,~] = cellfun(@fileparts,[m_files;p_files],'uni',false);
  % get listed classes:
  listed_classes = s.classes;
  % combine all potential class lists into one:
  cls_list = vertcat(name,listed_classes);
  % test which ones are actually classes:
  isClass = cellfun(@(x)exist(x,'class')==8,cls_list); %"exist" method; takes long
  %[u,ia,ic] = unique(ext(isClass(1:numel(ext)))); %DEBUG:

  % for valid classes, get metaclasses from name; if a classdef contains errors,
  % will cause cellfun to print the reason using ErrorHandler.
  [~] = cellfun(@meta.class.fromName,cls_list(isClass),'uni',false,'ErrorHandler',...
     @(ex,in)meta.class.empty(0*fprintf(1,'The classdef for "%s" contains an error: %s\n'...
                                         , in, ex.message)));
  % The result of the last computation used to be assigned into mc_list, but this 
  % is no longer required as the same information (and more) is returned later
  % by calling "mb_list = meta.class.getAllClasses" since these classes are now cached.
  clear cls_list isClass ind1 listed_classes m_files p_files name s
end
%% Get class list from classes belonging to packages (takes long!):

if includePackages
  % Get a list of all package classes:
  mp_list = meta.package.getAllPackages; mp_list = vertcat(mp_list{:});  
  % see http://www.mathworks.com/help/matlab/ref/meta.package.getallpackages.html

  % Recursively flatten package list:
  mp_list = flatten_package_list(mp_list);

  % Extract classes out of packages:
  mp_list = vertcat(mp_list.ClassList);
end
%% Combine lists:
% Get a list of all classes that are in memory:
mb_list = meta.class.getAllClasses; 
mc_list = union(vertcat(mb_list{:}), mp_list);
%% Map relations:
try
  [subcls_list,discovered_classes] = find_superclass_relations(mc_list);
  while ~isempty(discovered_classes)
    mc_list = union(mc_list, discovered_classes);
    [subcls_list,discovered_classes] = find_superclass_relations(mc_list);
  end
catch ex % Turns out this helps....
  disp(['Getting classes failed with error: ' ex.message ' Retrying...']);
  [mc_list,subcls_list] = q37829489;
end

end

function [subcls_list,discovered_classes] = find_superclass_relations(known_metaclasses)
%% Build hierarchy:
sup_list = {known_metaclasses.SuperclassList}.';
% Count how many superclasses each class has:
n_supers = cellfun(@numel,sup_list);
% Preallocate a Subclasses container: 
subcls_list = cell(numel(known_metaclasses),1); % should be meta.MetaData
% Iterate over all classes and 
% discovered_classes = meta.class.empty(1,0); % right type, but causes segfault
discovered_classes = meta.class.empty;
for depth = max(n_supers):-1:1
  % The function of this top-most loop was initially to build a hierarchy starting 
  % from the deepest leaves, but due to lack of ideas on "how to take it from here",
  % it only serves to save some processing by skipping classes with "no parents".
  tmp = known_metaclasses(n_supers == depth);
  for ind1 = 1:numel(tmp)
    % Fortunately, SuperclassList only shows *DIRECT* supeclasses. Se we
    % only need to find the superclasses in the known classees list and add
    % the current class to that list.
    curr_cls = tmp(ind1);
    % It's a shame bsxfun only works for numeric arrays, or else we would employ: 
    % bsxfun(@eq,mc_list,tmp(ind1).SuperclassList.');
    for ind2 = 1:numel(curr_cls.SuperclassList)
      pos = find(curr_cls.SuperclassList(ind2) == known_metaclasses,1);
      % Did we find the superclass in the known classes list?
      if isempty(pos)
        discovered_classes(end+1,1) = curr_cls.SuperclassList(ind2); %#ok<AGROW>
  %       disp([curr_cls.SuperclassList(ind2).Name ' is not a previously known class.']);
        continue
      end      
      subcls_list{pos} = [subcls_list{pos} curr_cls];
    end    
  end  
end
end

% The full flattened list for MATLAB R2016a contains about 20k classes.
function flattened_list = flatten_package_list(top_level_list)
  flattened_list = top_level_list;
  for ind1 = 1:numel(top_level_list)
    flattened_list = [flattened_list;flatten_package_list(top_level_list(ind1).PackageList)];
  end
end

Выходными данными этой функции являются 2 вектора, которые в терминах Java можно рассматривать какMap<meta.class, List<meta.class>>:

mc_list - объектный вектор классаmeta.classгде каждая запись содержит информацию об одном конкретном классе, известном MATLAB. Это «ключи» нашегоMap.subcls_list - (довольно редкий) вектор клеток, содержащий известныенепосредственный подклассы классов, появляющихся в соответствующей позицииmc_list, Это «ценности» нашегоMapкоторые по сутиList<meta.class>.

После того, как у нас есть эти два списка, нужно только найти позицию вашего класса интересов вmc_list и получить список его подклассов отsubcls_list, Если требуются косвенные подклассы, тот же процесс повторяется и для подклассов.

Альтернативно, можно представить иерархию, используя, например,logical sparse матрица смежности,A, гдеaI, J== 1 означает, что классi это подклассj, Тогда транспонирование этой матрицы может означать противоположное отношение, то естьaTI, J== 1 средстваi этосуперклассj, Помня об этих свойствах смежной матрицы, можно очень быстро осуществлять поиск и обход иерархии (избегая необходимости «дорогих» сравненийmeta.class объекты).

Несколько заметок:По неизвестным причинам (кеширование?) Код может не работать из-за ошибки (например,Invalid or deleted object.), в этом случае повторный запуск помогает. Я добавилtry/catch это делает это автоматически.В коде есть 2 случая, когда массивы растут внутри цикла. Это, конечно, нежелательно, и его следует избегать. Код был оставлен так из-за отсутствия лучших идей.Если нельзя избежать части «обнаружения» алгоритма (каким-то образом найти все классы в первую очередь), можно (и нужно) оптимизировать его так, чтобы каждая итерация работала только с ранее неизвестными классами.Интересныйнепреднамеренная выгода запуск этого кода в том, что он сканирует все известныеclassdefи сообщает о любых ошибках в них -Это может быть полезным инструментом для запуска время от времени для всех, кто работает в MATLAB OOP :)Спасибо @Suever за некоторые полезные советы.Сравнение с методом Олега:

Чтобы сравнить эти результаты с примером Олега, я буду использовать вывод запуска вышеуказанного сценария на моем компьютере (содержащий ~ 20 тыс. Классов; загруженоВот как.mat файл). Затем мы можем получить доступ к карте классов следующим образом:

hRoot = meta.class.fromName('sde');
subcls_list{mc_list==hRoot}

ans = 

  class with properties:

                     Name: 'sdeddo'
              Description: ''
      DetailedDescription: ''
                   Hidden: 0
                   Sealed: 0
                 Abstract: 0
              Enumeration: 0
          ConstructOnLoad: 0
         HandleCompatible: 0
          InferiorClasses: {0x1 cell}
        ContainingPackage: [0x0 meta.package]
             PropertyList: [9x1 meta.property]
               MethodList: [18x1 meta.method]
                EventList: [0x1 meta.event]
    EnumerationMemberList: [0x1 meta.EnumeratedValue]
           SuperclassList: [1x1 meta.class]

subcls_list{mc_list==subcls_list{mc_list==hRoot}} % simulate recursion

ans = 

  class with properties:

                     Name: 'sdeld'
              Description: ''
      DetailedDescription: ''
                   Hidden: 0
                   Sealed: 0
                 Abstract: 0
              Enumeration: 0
          ConstructOnLoad: 0
         HandleCompatible: 0
          InferiorClasses: {0x1 cell}
        ContainingPackage: [0x0 meta.package]
             PropertyList: [9x1 meta.property]
               MethodList: [18x1 meta.method]
                EventList: [0x1 meta.event]
    EnumerationMemberList: [0x1 meta.EnumeratedValue]
           SuperclassList: [1x1 meta.class]

Здесь мы можем видеть, что последний вывод только 1 класс (sdeld), когда мы ожидали 3 из них (sdeld,sdemrd,heston) - это означает, чтонекоторые классыне хватает из этого списка1.

Напротив, если мы проверяем общий родительский класс, такой какhandleмы видим совершенно другую картину:

subcls_list{mc_list==meta.class.fromName('handle')}

ans = 

  1x4059 heterogeneous class (NETInterfaceCustomMetaClass, MetaClassWithPropertyType, MetaClass, ...) array with properties:

    Name
    Description
    DetailedDescription
    Hidden
    Sealed
    Abstract
    Enumeration
    ConstructOnLoad
    HandleCompatible
    InferiorClasses
    ContainingPackage
    PropertyList
    MethodList
    EventList
    EnumerationMemberList
    SuperclassList

В заключение несколько слов: этот метод пытается индексироватьвсе известные классы на пути MATLAB. Создание списка / индекса класса занимает несколько минут, но это однократный процесс, который окупается позже при поиске в списке. Кажется, что пропущены некоторые классы, но найденные отношения не ограничиваются одними и теми же пакетами, путями и т. Д. По этой причине он по своей природе поддерживает множественное наследование.

1 -Я в настоящее время понятия не имею, что вызывает это.

 Dev-iL28 июн. 2016 г., 18:20
@ Олег Хорошо, просто вставьте первый блок и второй блок в один файл (q37829489.m) один за другим, убедитесь, что вы назначаете оба выхода и отключаетеdbstop if error если у вас есть это.
 Oleg28 июн. 2016 г., 20:43
Я просто хотел предположить, что в ваших интересах дать простые инструкции и продемонстрировать, как использовать ваше решение.
 Dev-iL28 июн. 2016 г., 15:23
@Ratbert Это интересная проблема! Возможно, всем нам нужно объединить усилия (также @Suever), открыть репозиторий Github, как предложил Олег, объединить все коды и превратить его в приятный и полный инструмент. ... и спасибо за щедрость :)
 Suever24 июн. 2016 г., 16:18
Это работает для классов, которые живут внутри пакета?
 Dev-iL28 июн. 2016 г., 18:35
@Ratbert Я хотел бы отметить, что долгое время выполнениятребуется только один разДо тех пор, пока в следующий раз классы, видимые для MATLAB, не изменятся. После того, как вы запустили код в течение 2 минут (или сколько бы времени это ни занимало в вашей системе), вы получите два вектора метаклассов, и их обработка должна быть практически мгновенной. Затем вы можете сохранить эти векторы где-нибудь (или логический массив, который я упомянул, можно построить из них) и просто запроситьтот результат.
 Dev-iL29 июн. 2016 г., 00:31
@ Олег, см. Отредактированный ответ. Надеюсь, то, что я делал, теперь стало понятнее.
 Oleg28 июн. 2016 г., 18:08
Да, я согласен, в текущей форме я не могу запустить ваш код. Я хотел сравнить с моими результатами.
 Ratbert28 июн. 2016 г., 15:17
@ Dev-iL: спасибо, что потратили столько времени на этот вопрос. Хотя ваш ответ все еще имеет некоторые ограничения (например, скорость), это уже хороший кусок кода.
 Suever24 июн. 2016 г., 16:16
Я бы порекомендовал использоватьpathsep разделить выводpath у вас нет никаких кроссплатформенных проблем. Хорошее решение, хотя. Также в дополнение ко всем папкам на пути, я бы посмотрел вpwd так как это технически на пути, а также.
 Oleg28 июн. 2016 г., 18:13
Нет, я просто не получаю то, что мне нужно для копирования-вставки, в каком порядке и что нужно запускать первым.
 Dev-iL28 июн. 2016 г., 18:08
@ Олег, пожалуйста, объясни. Вы получаете ошибку или как? Не стесняйтесь приходить вчат чтобы обсудить это.

Ваш ответ на вопрос