matlab - 有没有办法找到一个 Matlab 类的所有 child ?

标签 matlab oop subclass class-hierarchy

Matlab 函数 superclasses 返回给定类的所有父类的名称。

是否有等效的查找从给定类派生的所有类,即子类?函数 allchild 似乎仅限于图形句柄。

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

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

最佳答案

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.



由于我们要查找全部 子类,确定的方法是列出 所有已知 类,然后检查每个类是否派生自其他类。为了实现这一点,我们将我们的工作分为两类:
  • “批量类(class)” - 在这里我们使用 what 函数来创建一个文件列表,这些文件只是在 MATLAB 路径上“放置”,它输出一个结构体 s (在 what 的文档中描述,具有以下字段: 'path' 'm' 'mlapp' 'mat' 'mex' 'mdl' 'slx' 'p' 'classes' 'packages' 。然后我们将选择其中的一些来构建类列表。为了识别 .m 或 .p 文件具有什么样的内容(我们关心的是类/非类),我们使用 exist . This method is demonstrated by Loren in her blog 。在我的代码中,这是 mb_list .
  • “包类” - 这包括由 MATLAB 索引作为其内部包结构的一部分的类文件。获取此列表所涉及的算法涉及调用 meta.package.getAllPackages 然后递归遍历这个顶级包列表,得到所有的子包。然后从每个包中提取一个类列表,将所有列表连接成一个长列表 - mp_list .

  • 该脚本有两个输入标志( includeBulkFilesincludePackages ),用于确定是否应将每种类型的类包含在输出列表中。

    完整代码如下 :
    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 的“ key ” .
  • subcls_list - 一个(相当稀疏的)细胞向量,包含已知 直销 mc_list对应位置出现的类的子类.这些是我们 Map 的“值(value)观” ,本质上是 List<meta.class> .

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

    或者,可以使用例如表示层次结构。 logical sparse邻接矩阵,A ,其中 ai,j==1 表示该类 ij 的子类.那么这个矩阵的转置可以表示相反的关系,即aTi,j==1表示i super j类(class).记住邻接矩阵的这些属性可以非常快速地搜索和遍历层次结构(避免需要对 meta.class 对象进行“昂贵”的比较)。

    几个注意事项:
  • 由于未知原因(缓存?),代码可能会由于错误(例如 Invalid or deleted object. )而失败,在这种情况下重新运行它会有所帮助。我添加了一个 try/catch这会自动执行此操作。
  • 代码中有 2 个实例,其中数组在循环内增长。这当然是不需要的,应该避免。由于缺乏更好的想法,代码就这样留下了。
  • 如果无法避免算法的“发现”部分(首先通过某种方式找到所有类),则可以(并且应该)对其进行优化,以便每次迭代仅对以前未知的类进行操作。
  • 运行此代码的一个有趣的意外好处是它会扫描所有已知的 classdef s 并报告其中的任何错误 - 对于在 MATLAB OOP 上工作的任何人来说,这可能是一个有用的工具,每隔一段时间运行一次 :)
  • 感谢@Suever 提供一些有用的指针。


  • 与Oleg的方法比较:

    为了将这些结果与 Oleg 的示例进行比较,我将使用在我的计算机上运行上述脚本的输出(包含 ~20k 类;上传 here 作为 .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 个( sdeldsdemrdheston )——这意味着某些类 丢失 从这个列表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 次的过程,稍后在搜索列表时会得到返回。它似乎遗漏了一些类,但发现的关系并不限于相同的包、路径等。因此,它本质上支持多重继承。

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

    关于matlab - 有没有办法找到一个 Matlab 类的所有 child ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37829489/

    相关文章:

    java - 如果 ValueOF 或 Integer.parseInt 不起作用,如何在 Java 中划分两个字符串?

    java - Java 是否支持类似 Swift 的类扩展?

    python - 什么时候调用 Python 的 super().__init__()?

    swift - 具有 IBInspectable 颜色属性的子类 - 无法覆盖

    matlab - 在 MATLAB 中的许多图像上叠加时间戳

    matlab - 将图例与轴对象匹配

    python - 设计异常继承的标准方法是什么?

    matlab - 如何在 MATLAB 中显示静态分析警告?

    matlab - 如何根据特定行(Matlab)中包含的条件对列进行排序?

    php - 在对象数组中取消设置对象