有没有办法找到Matlab类的所有子级? [英] Is there a way to find all children of a Matlab class?

查看:115
本文介绍了有没有办法找到Matlab类的所有子级?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Matlab函数 superclasses 返回给定的所有父项的名称课.

The Matlab function superclasses returns the names of all parents of a given class.

是否存在从给定类 i.e.子类中找到所有派生的类的等效项?函数 allchild 似乎仅限于图形句柄.

Is there an equivalent to find all classes derived from a given class, i.e. children classes ? The function allchild seems to be restricted to graphical handles.

如果没有,可以采用什么策略来获得这样的列表?蛮力路径扫描是唯一的选择吗?

If not, what strategy could be adopted to get such a list ? Is brute force path scanning the only option ?

让我们将自己限制在Matlab路径中的类上.

Let's restrict ourselves to the classes in Matlab's path.

推荐答案

简介:

在解决方案过程中,我似乎找到了一个未记录的meta.class类静态方法,该方法返回所有缓存的类(当有人调用clear classes时,几乎擦除了所有内容),并且还(完全是偶然地)制作了一个检查classdef文件是否存在错误的工具.

Intro:

During the course of the solution I seem to have found an undocumented static method of the meta.class class which returns all cached classes (pretty much everything that gets erased when somebody calls clear classes) and also (entirely by accident) made a tool that checks classdef files for errors.


由于我们要查找 所有 子类,因此确定的方法是列出所有 已知 类,然后检查每一个是否从其他任何一个派生.为此,我们将工作分为两种类型:


Since we want to find all subclasses, the sure way to go is by making a list of all known classes and then checking for each one if it's derived from any other one. To achieve this we separate our effort into 2 types of classes:

  • "Bulk classes" - here we employ the what function to make a list of files that are just "laying around" on the MATLAB path, which outputs a structure s (described in the docs of what having the following fields: 'path' 'm' 'mlapp' 'mat' 'mex' 'mdl' 'slx' 'p' 'classes' 'packages'. We will then select some of them to build a list of classes. To identify what kind of contents an .m or a .p file has (what we care about is class/not-class), we use exist. This method is demonstrated by Loren in her blog. In my code, this is mb_list.
  • "Package classes" - this includes class files that are indexed by MATLAB as part of its internal package structure. The algorithm involved in getting this list involves calling meta.package.getAllPackages and then recursively traversing this top-level package list to get all sub-packages. Then a class list is extracted from each package, and all lists are concatenated into one long list - mp_list.

该脚本具有两个输入标志(includeBulkFilesincludePackages),这些标志确定是否应在输出列表中包括每种类型的类.

The script has two input flags (includeBulkFiles,includePackages) that determine whether each type of classes should be included in the output list.

完整代码如下:

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>>:

The outputs of this function are 2 vectors, who in Java terms can be thought of as a Map<meta.class, List<meta.class>>:

  • mc_list - an object vector of class meta.class, where each entry contains information about one specific class known to MATLAB. These are the "keys" of our Map.
  • subcls_list - A (rather sparse) vector of cells, containing known direct subclasses of the classes appearing in the corresponding position of mc_list. These are the "values" of our Map, which are essentially List<meta.class>.

一旦有了这两个列表,只需查找感兴趣的类在mc_list中的位置,然后从subcls_list中获取其子类的列表即可.如果需要间接子类,则对子类也重复相同的过程.

Once we have these two lists, it's only a matter of finding the position of your class-of-interest in mc_list and getting the list of its subclasses from subcls_list. If indirect subclasses are required, the same process is repeated for the subclasses too.

或者,可以使用例如logical sparse邻接矩阵A,其中 a i,j == 1 表示类ij的子类.那么此矩阵的转置可以表示相反的关系,即 a T i,j == 1 表示i是一个>的超级类.牢记邻接矩阵的这些属性可以非常快速地搜索和遍历层次结构(避免对meta.class对象进行昂贵"的比较).

Alternatively, one can represent the hierarchy using e.g. a logical sparse adjacency matrix, A, where ai,j==1 signifies that class i is a subclass of j. Then the transpose of this matrix can signify the opposite relation, that is, aTi,j==1 means i is a superclass of j. Keeping these properties of the adjaceny matrix in mind allows very rapid searches and traversals of the hierarchy (avoiding the need for "expensive" comparisons of meta.class objects).

  • 由于未知的原因(缓存?),代码可能由于错误(例如Invalid or deleted object.)而失败,在这种情况下,重新运行它会有所帮助.我添加了一个自动执行此操作的try/catch.
  • 在代码中有2个实例,它们在一个循环内增长数组.这当然是不希望的,应该避免.由于缺乏更好的主意,因此代码被保留下来了.
  • 如果无法避免算法的发现"部分(首先通过某种方式找到所有类),则可以(并且应该)对其进行优化,以使每次迭代都只能对先前未知的类进行操作.
  • 运行此代码的一个有趣的意想不到的好处是它会扫描所有已知的classdef并报告其中的任何错误-这是一个非常有用的工具,可以在一个环境中一次运行一次对于任何使用MATLAB OOP的人:)
  • 感谢@Suever提供一些有用的指针.
  • For reasons unknown (caching?) the code may fail due to an error (e.g. Invalid or deleted object.), in that case re-running it helps. I have added a try/catch that does this automatically.
  • There are 2 instances in the code where arrays are grown inside a loop. This is of course unwanted and should be avoided. The code was left like that due to a lack of better ideas.
  • If the the "discovery" part of the algorithm cannot be avoided (by somehow finding all the classes in the first place), one can (and should) optimize it so that every iteration only operates on previously unknown classes.
  • An interesting unintended benefit of running this code is that it scans all known classdefs and reports any errors in them - this can be a useful tool to run every once in a while for anyone who works on MATLAB OOP :)
  • Thanks @Suever for some helpful pointers.

为了将这些结果与Oleg的示例进行比较,我将在计算机上使用上述脚本的运行结果(包含约20k个类;已上传

To compare these results with Oleg's example, I will use the output of a run of the above script on my computer (containing ~20k classes; uploaded here as a .mat file). We can then access the class map the following way:

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个类(sdeldsdemrdheston)时-这意味着一些类从该列表 1 丢失.

Here we can see that the last output is only 1 class (sdeld), when we were expecting 3 of them (sdeld,sdemrd,heston) - this means that some classes are missing from this list1.

相反,如果我们检查诸如handle之类的公共父类,则会看到完全不同的画面:

In contrast, if we check a common parent class such as handle, we see a completely different picture:

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步的过程,稍后会在搜索列表时得到回报.似乎错过了一些类,但是找到的关系并不限于相同的程序包,路径等.因此,它固有地支持多重继承.

To conclude this in several words: this method attempts to index all known classes on the MATLAB path. Building the class list/index takes several minutes, but this is a 1-time process that pays off later when the list is searched. It seems to miss some classes, but the found relations are not restricted to the same packages, paths etc. For this reason it inherently supports multiple inheritance.

1-我目前不知道是什么原因造成的.

这篇关于有没有办法找到Matlab类的所有子级?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆