如何解决是否可以从旧式类派生出 classdef 类?
是否可以从旧式类派生出 classdef 类?
在 Octave 4.0 中,ss
类是在旧样式中定义的,带有 @ss
目录。
我想在位于 __freqresp__
的文件 myss.m
中使用自定义 path
方法派生一个类,并具有以下内容:
classdef myss < ss
methods
function x = solve_svd(A,b)
[U,S,V] = svd(A);
si = 1.0 ./ diag(S);
si(~isfinite(si)) = 0;
x = V' * diag(si) * U'*b;
endfunction
function H = __freqresp__ (sys,w,cellflag = false)
if (sys.scaled == false)
sys = prescale (sys);
endif
[a,b,c,d,e,tsam] = dssdata (sys);
if (isct (sys)) # continuous system
s = i * w;
else # discrete system
s = exp (i * w * abs (tsam));
endif
H = arrayfun (@(x) c*solve_svd(s*E-A,b) + d,s,"uniformoutput",false);
if (! cellflag)
H = cat (3,H{:});
endif
endfunction
endmethods
endclassdef
遗憾的是,即使加载了 ss
包,Octave 也会抱怨未知类 control
。
解决方法
(我在这里从 MATLAB 的角度回答,因为这是我最了解的,但 Octave 在这里具有完全相同的行为,因此它同样适用于 Octave。)
@
样式类的问题在于,MATLAB 在创建类的对象之前不知道它们的属性。因此,要使用旧式类作为基类,MATLAB 必须构造一个基对象来了解类的外观,但是使用错误的输入调用构造函数可能会导致错误消息。或者构造函数可以完成数小时的工作。仅仅为了了解对象的外观而构建对象是不可行的。
我认为这是引入 classdef
样式类的核心原因。还有其他改进,但没有一个比这更重要。在 @
风格的类中,继承是在对象创建时确定的,必须先手动创建基类的对象,然后将这些对象合并到正在创建的派生对象中。
以下是 @
样式类行为的一个有趣示例,该示例使其无法用作 classdef
样式类中的基类:
@foo/foo.m:
function obj = foo(x)
if x
obj = class(struct('a','a'),'foo');
else
obj = class(struct('b','b'),'foo');
end
现在我们在 MATLAB 中输入:
>> a=foo(0)
a =
foo object: 1-by-1
>> b=foo(1)
Error using class
[...]
>> clear classes
>> b=foo(1)
b =
foo object: 1-by-1
>> a=foo(0)
Error using class
[...]
类 foo
的变化取决于类的第一个对象的创建方式。一旦我们以一种方式创建了一个对象,另一种方式就变得非法了。
OP 原始问题的解决方案:
在 Octave 4.0 中,ss
类是在旧样式中定义的,带有 @ss
目录。我想用自定义的 __freqresp__
方法 [...] 派生一个类。
与其派生一个带有自定义方法的新类,不如考虑覆盖现有方法。只需创建一个目录@ss
,并在其中放置一个文件__freqresp__.m
。确保您的 @ss
目录位于 Octave 路径上的目录中,该目录位于原始类所在的工具箱目录之前。
我假设原始 __freqresp__.m
是一个实际方法,而不是类的 private
子目录中的函数。如果是这样,则它不是一种方法并且不能被覆盖(请参阅 Octave 手册中的 Function Precedence)。
请注意,您可以覆盖任何类型的重载函数,甚至是内置类型。例如,您可以创建一个函数 @double/length.m
以在使用普通(双)矩阵调用时覆盖 length
函数。
将我在评论中的回答扩展为一个例子。
就像我在评论中所说的,我认为混合 classdef 和经典的面向对象风格是不可能的。因此,我会使用“经典”的 OO 风格来代替执行“重载”,这无论如何都相当简单。
然而,与 matlab/octave 中的许多其他语法一样,经典风格使用文件系统语义来定义各自的命名空间,并且您有额外的要求,即您希望在单个文件中编写所有这些功能。
然后想到的显而易见的解决方法是在文件系统上“即时”创建必要的文件/命名空间,然后稍后在主代码中适当地加载该类定义。显然,这并不是真正推荐的处理类(新或旧)的方法,但是,如果您确实必须从单个文件中工作,那么它不是也很难做到。
这是一个(独立的)示例来演示这一点(即将代码复制到脚本中,然后以八度音程运行脚本):
% Create class structure in a temporary directory
ClassPath = tempname();
ClassDir = fullfile( ClassPath,'@myss' );
Constructor_m = fullfile( ClassDir,'myss.m' );
Freqresp_m = fullfile( ClassDir,'__freqresp__.m' );
mkdir( ClassPath )
mkdir( ClassDir )
% Define constructor
f = fopen( Constructor_m,'w' );
fdisp( f,' function Out = myss( sys ) ' );
fdisp( f,' Out = struct(); ' );
fdisp( f,' Out = class( Out,"myss",sys ); ' );
fdisp( f,' endfunction ' );
fclose(f);
% Define overriding __freqresp__ method
f = fopen( Freqresp_m,' function H = __freqresp__ (sys,w,cellflag = false) ' );
fdisp( f,' ' );
fdisp( f,' if get( sys,"scaled" ) == false ' );
fdisp( f,' sys = myss( prescale (sys) ); ' );
fdisp( f,' endif ' );
fdisp( f,' [a,b,c,d,e,tsam] = dssdata (sys); ' );
fdisp( f,' if (isct (sys)) # continuous system ' );
fdisp( f,' s = i * w; ' );
fdisp( f,' else # discrete system ' );
fdisp( f,' s = exp (i * w * abs (tsam)); ' );
fdisp( f,' H = arrayfun (@(x) c*solve_svd(s*e-a,b) + d,s,"uniformoutput",false); ' );
fdisp( f,' if (! cellflag) ' );
fdisp( f,' H = cat (3,H{:}); ' );
fdisp( f,' endfunction ' );
fclose(f);
% Load class definition
addpath( ClassPath );
% Note: this does not need to be a class member (technically it wasn't one before either).
function x = solve_svd(A,b)
[U,S,V] = svd(A);
si = 1.0 ./ diag(S);
si(~isfinite(si)) = 0;
x = V' * diag(si) * U'*b;
endfunction
% Main code using derived 'myss' class.
pkg load control
a = [1,2,3; 4,5,6; 7,8,9];
b = [10;11;12];
stname = {'V','A','kJ' };
sys = ss( a,'stname',stname );
mysys = myss( sys );
disp( 'The freqresp using the overriden __freqresp__ method is:' );
disp( __freqresp__( mysys,5 ) );
,
没有。新式 classdef
("MCOS") 类与旧式类是一种不同的机制,它们不能通过继承来组合。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。