Matlab函数superclasses
返回给定类的所有父类名称。
是否有一个等价的方法可以找到所有派生自给定类的子类?函数allchild
似乎只限于图形句柄。
如果没有,哪种策略可以用来获取这样的列表?是否只能采用暴力路径扫描?
让我们将注意力集中在Matlab路径下的类上。
Matlab函数superclasses
返回给定类的所有父类名称。
是否有一个等价的方法可以找到所有派生自给定类的子类?函数allchild
似乎只限于图形句柄。
如果没有,哪种策略可以用来获取这样的列表?是否只能采用暴力路径扫描?
让我们将注意力集中在Matlab路径下的类上。
在解决方案的过程中,我似乎发现了 meta.class
类的一个未记录的静态方法,它返回所有缓存的类(当某人调用 clear classes
时会擦除几乎所有内容),并且(完全意外地)制作了一个检查 classdef
文件错误的工具。
由于我们想要找到 所有 子类,最可靠的方法是创建一个列表,包含所有已知的类,然后检查每个类是否派生自其他类。为了实现这一点,我们将我们的努力分为两种类型的类:
what
函数来列出“躺在” MATLAB 路径上的文件列表,输出一个结构体 s
(在 what
的文档中描述)其中包含以下字段:'path' 'm' 'mlapp' 'mat' 'mex' 'mdl' 'slx' 'p' 'classes' 'packages'
。然后我们将从中选择一些文件来构建类列表。为了识别 .m 或 .p 文件包含的内容类型(我们关心的是类/非类),我们使用exist
函数。在Loren 的博客中演示了这种方法。在我的代码中,它被称为 mb_list
。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
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提供了一些有用的指针。
为了将这些结果与Oleg的示例进行比较,我将使用在我的计算机上运行上述脚本的输出(其中包含大约20k个类;作为.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]
sdeld
),而我们预期会有三个类(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
pathsep
来分割 path
的输出,这样就不会有跨平台问题了。不过你的解决方案也很好。另外除了路径上所有的文件夹,我还会查看 pwd
,因为它在技术上也在路径上。 - Sueverq37829489.m
)中,一个接一个地放置,确保您分配了两个输出,并在打开dbstop if error
时禁用它。 - Dev-iL由于代码超过200行,我已将其移至Github存储库getSubclasses。欢迎您提出功能需求和错误报告。
给定一个根类名或meta.class
和文件夹路径,它将遍历文件夹结构并建立一个包含所有从根派生的子类(无限深度)的图形。如果未提供路径,则会从根类所在的文件夹递归下降。
请注意,该解决方案是本地的,因此速度很快,并且基于子类嵌套在所选路径的某个子文件夹下的假设。
列出sde
类的所有子类。您需要R2015b才能生成图形,或者您可以使用输出和FEX提交plot_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
在Win7 64位 R2016a上
getSubclasses('sde','C:\Program Files\MATLAB\R2016a\toolbox\finance\finsupport')
getSubclasses('sde',matlabroot);
不是完整的解决方案,但您可以将路径中所有.m
文件解析为文本,并使用正则表达式查找子类定义。
例如:^\s*classdef\s*(\w*)\s*<\s*superClassName\s*(%.*)?
请注意,这种方法对于任何使用高级特性(例如eval
)的子类定义会出现静默失败。