是否可以从旧式 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 类 是不同的机制,不能通过继承组合。