是否可以从旧式 class 派生出 classdef class?
Is it possible to derive a classdef class from an old-style class?
是否可以从旧式 class 派生出 classdef class?
在 Octave 4.0 中,ss
class 以旧样式定义在 @ss
目录中。
我想在位于 path
的文件 myss.m
中使用自定义 __freqresp__
方法派生一个 class 并具有以下内容:
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
遗憾的是,即使加载了 control
包,Octave 也会抱怨未知 class ss
。
将我在评论中的回答扩展到一个例子。
正如我在评论中所说,我认为混合 classdef 和 classic 面向对象的样式是不可能的。因此,我会使用 'classic' OO 风格来执行 'overload',无论如何这都相当简单。
然而,与 matlab/octave 中的许多其他语法一样,classic 风格使用文件系统语义来定义各自的名称空间,并且您有一个额外的要求,即您希望对所有代码进行编码在单个文件中包含此功能。
然后想到的明显解决方法是在文件系统 'on the fly' 上创建必要的 files/namespaces,然后稍后在您的主代码中适当地加载该 class 定义.显然,这并不是真正推荐使用 classes(新 或 旧)的方法,但是,如果你真的 必须 从单个文件开始工作,那么完成两者都不难。
这是一个(独立的)示例来演示这一点(即将代码复制到脚本中,并且只是 运行 八度的脚本):
% 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, 'w' );
fdisp( f, ' 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, ' ' );
fdisp( f, ' [a, b, c, d, e, tsam] = dssdata (sys); ' );
fdisp( f, ' ' );
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, ' endif ' );
fdisp( f, ' ' );
fdisp( f, ' H = arrayfun (@(x) c*solve_svd(s*e-a,b) + d, s, "uniformoutput", false); ' );
fdisp( f, ' ' );
fdisp( f, ' if (! cellflag) ' );
fdisp( f, ' H = cat (3, H{:}); ' );
fdisp( f, ' endif ' );
fdisp( f, ' ' );
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, b, 'stname', stname );
mysys = myss( sys );
disp( 'The freqresp using the overriden __freqresp__ method is:' );
disp( __freqresp__( mysys, 5 ) );
(我在这里从 MATLAB 的角度回答,因为这是我最了解的,但是 Octave 在这里有完全相同的行为,所以它同样适用于 Octave。)
@
风格 classes 的问题是 MATLAB 在创建 class 的对象之前不知道它们的属性。因此,要使用旧式 class 作为基础 class,MATLAB 必须构造一个基础对象来了解 class 的样子,但调用构造函数时输入错误可能导致错误消息。或者构造函数可以完成数小时的工作。仅仅为了了解它的外观而构建对象是不可行的。
我认为这是引入 classdef
风格的 classes 的核心原因。还有其他改进,但 none 与此一样重要。在@
-style classes中,继承是在创建对象时确定的,必须先手动创建基础class的对象,然后将它们合并到正在创建的派生对象中。
这里有一个有趣的例子,说明了 @
风格的 class 行为,使得它无法在 classdef
风格的 class 中用作基础 class:
@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
[...]
Class foo
根据 class 的第一个对象的创建方式而变化。一旦我们以一种方式创建了一个对象,另一种方式就变得不合法了。
OP原问题的解决方案:
In Octave 4.0 the ss
class is defined in the old style with @ss
directory. I want to derive a class with a customized __freqresp__
method [...].
与其 派生 具有自定义方法的新 class,不如考虑 覆盖 现有方法。只需创建一个目录 @ss
,然后将文件 __freqresp__.m
放入其中。确保您的 @ss
目录位于 位于 原始 class 所在的工具箱目录之前的 Octave 路径上的目录中。
我假设原始 __freqresp__.m
是一个实际方法,而不是 class' private
子目录中的函数。如果是这样,它不是一个方法并且不能被覆盖(参见 Octave 手册中的 Function Precedence)。
请注意,您可以覆盖任何类型的重载函数,甚至是内置类型。例如,您可以创建函数 @double/length.m
以在使用普通(双精度)矩阵调用时覆盖 length
函数。
没有。 New-style classdef
("MCOS") 类 与old-style 类 是不同的机制,不能通过继承组合。
是否可以从旧式 class 派生出 classdef class?
在 Octave 4.0 中,ss
class 以旧样式定义在 @ss
目录中。
我想在位于 path
的文件 myss.m
中使用自定义 __freqresp__
方法派生一个 class 并具有以下内容:
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
遗憾的是,即使加载了 control
包,Octave 也会抱怨未知 class ss
。
将我在评论中的回答扩展到一个例子。
正如我在评论中所说,我认为混合 classdef 和 classic 面向对象的样式是不可能的。因此,我会使用 'classic' OO 风格来执行 'overload',无论如何这都相当简单。
然而,与 matlab/octave 中的许多其他语法一样,classic 风格使用文件系统语义来定义各自的名称空间,并且您有一个额外的要求,即您希望对所有代码进行编码在单个文件中包含此功能。
然后想到的明显解决方法是在文件系统 'on the fly' 上创建必要的 files/namespaces,然后稍后在您的主代码中适当地加载该 class 定义.显然,这并不是真正推荐使用 classes(新 或 旧)的方法,但是,如果你真的 必须 从单个文件开始工作,那么完成两者都不难。
这是一个(独立的)示例来演示这一点(即将代码复制到脚本中,并且只是 运行 八度的脚本):
% 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, 'w' );
fdisp( f, ' 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, ' ' );
fdisp( f, ' [a, b, c, d, e, tsam] = dssdata (sys); ' );
fdisp( f, ' ' );
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, ' endif ' );
fdisp( f, ' ' );
fdisp( f, ' H = arrayfun (@(x) c*solve_svd(s*e-a,b) + d, s, "uniformoutput", false); ' );
fdisp( f, ' ' );
fdisp( f, ' if (! cellflag) ' );
fdisp( f, ' H = cat (3, H{:}); ' );
fdisp( f, ' endif ' );
fdisp( f, ' ' );
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, b, 'stname', stname );
mysys = myss( sys );
disp( 'The freqresp using the overriden __freqresp__ method is:' );
disp( __freqresp__( mysys, 5 ) );
(我在这里从 MATLAB 的角度回答,因为这是我最了解的,但是 Octave 在这里有完全相同的行为,所以它同样适用于 Octave。)
@
风格 classes 的问题是 MATLAB 在创建 class 的对象之前不知道它们的属性。因此,要使用旧式 class 作为基础 class,MATLAB 必须构造一个基础对象来了解 class 的样子,但调用构造函数时输入错误可能导致错误消息。或者构造函数可以完成数小时的工作。仅仅为了了解它的外观而构建对象是不可行的。
我认为这是引入 classdef
风格的 classes 的核心原因。还有其他改进,但 none 与此一样重要。在@
-style classes中,继承是在创建对象时确定的,必须先手动创建基础class的对象,然后将它们合并到正在创建的派生对象中。
这里有一个有趣的例子,说明了 @
风格的 class 行为,使得它无法在 classdef
风格的 class 中用作基础 class:
@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
[...]
Class foo
根据 class 的第一个对象的创建方式而变化。一旦我们以一种方式创建了一个对象,另一种方式就变得不合法了。
OP原问题的解决方案:
In Octave 4.0 the
ss
class is defined in the old style with@ss
directory. I want to derive a class with a customized__freqresp__
method [...].
与其 派生 具有自定义方法的新 class,不如考虑 覆盖 现有方法。只需创建一个目录 @ss
,然后将文件 __freqresp__.m
放入其中。确保您的 @ss
目录位于 位于 原始 class 所在的工具箱目录之前的 Octave 路径上的目录中。
我假设原始 __freqresp__.m
是一个实际方法,而不是 class' private
子目录中的函数。如果是这样,它不是一个方法并且不能被覆盖(参见 Octave 手册中的 Function Precedence)。
请注意,您可以覆盖任何类型的重载函数,甚至是内置类型。例如,您可以创建函数 @double/length.m
以在使用普通(双精度)矩阵调用时覆盖 length
函数。
没有。 New-style classdef
("MCOS") 类 与old-style 类 是不同的机制,不能通过继承组合。