有没有办法找到Matlab类的所有子级? [英] Is there a way to find all children of a Matlab class?
问题描述
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 callsclear classes
) and also (entirely by accident) made a tool that checksclassdef
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:
- "批量课程"-在这里,我们使用
what
函数创建一个仅在MATLAB路径上环绕"的文件列表,该文件列表输出结构s
(在exist
.包装类"-这包括由MATLAB索引的类文件,作为其内部包装结构的一部分.获取此列表所涉及的算法涉及调用meta.package.getAllPackages
,然后递归遍历此顶级软件包列表以获取所有子软件包.然后从每个包中提取一个类列表,并将所有列表连接到一个长列表-mp_list
.
- "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 structures
(described in the docs ofwhat
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 useexist
. This method is demonstrated by Loren in her blog. In my code, this ismb_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
.
该脚本具有两个输入标志(includeBulkFiles
,includePackages
),这些标志确定是否应在输出列表中包括每种类型的类.
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
-类meta.class
,其中每个条目都包含有关MATLAB已知的一个特定类的信息.这些是我们Map
的键". -
subcls_list
-细胞的(稀疏)向量,包含出现在mc_list
对应位置的类的已知 direct 子类.这些是我们Map
的值",本质上是List<meta.class>
.
mc_list
- an object vector of classmeta.class
, where each entry contains information about one specific class known to MATLAB. These are the "keys" of ourMap
.subcls_list
- A (rather sparse) vector of cells, containing known direct subclasses of the classes appearing in the corresponding position ofmc_list
. These are the "values" of ourMap
, which are essentiallyList<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 表示类i
是j
的子类.那么此矩阵的转置可以表示相反的关系,即 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 atry/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
classdef
s 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个类(sdeld
,sdemrd
,heston
)时-这意味着一些类从该列表 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屋!