Skip to content

Commit

Permalink
Add mp_table_subclass.
Browse files Browse the repository at this point in the history
  • Loading branch information
rdzman committed Dec 20, 2023
1 parent d37bacb commit 346efa6
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 50 deletions.
1 change: 1 addition & 0 deletions docs/sphinx/source/ref-manual/classes/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ Miscellaneous Classes
:name: sec_misc_classes

mp_table
mp_table_subclass
mp/element_container
mp/mapped_array
mp/NODE_TYPE
Expand Down
10 changes: 10 additions & 0 deletions docs/sphinx/source/ref-manual/classes/mp_table_subclass.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.. automodule:: matpower

:raw-html:`<div style="float: right"><a href="https://github.com/MATPOWER/matpower/blob/master/lib/mp_table_subclass.m" target=_blank><svg height="32" aria-hidden="true" viewBox="0 0 16 16" version="1.1" width="32" data-view-component="true" class="octicon octicon-mark-github v-align-middle color-fg-default"><path d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"></path></svg></a></div>`

mp_table_subclass
-----------------

.. autoclass:: mp_table_subclass
:show-inheritance:
:members:
23 changes: 16 additions & 7 deletions lib/mp_table.m
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@
% ::
%
% T = mp_table(var1, var2, ...);
% T = mp_table(..., 'VariableNames', {name1, name2, ...}});
% T = mp_table(..., 'RowNames', {name1, name2, ...}});
% T = mp_table(..., 'DimensionNames', {name1, name2, ...}});
% T = mp_table(..., 'VariableNames', {name1, name2, ...});
% T = mp_table(..., 'RowNames', {name1, name2, ...});
% T = mp_table(..., 'DimensionNames', {name1, name2, ...});
args = varargin;

if nargin
%% extract named arguments
[var_names, row_names, dim_names, args] = ...
extract_named_args(obj, args);
mp_table.extract_named_args(args);

%% set default variable names
nv = length(args); %% number of variables
Expand Down Expand Up @@ -623,10 +623,19 @@ function display(obj)
end
end %% methods (public)

methods (Access=protected)
methods (Static)
function [var_names, row_names, dim_names, args] = ...
extract_named_args(obj, args)
% used to extract named arguments pass to constructor
extract_named_args(args)
% Extracts special named constructor arguments.
% ::
%
% [var_names, row_names, dim_names, args] = extract_named_args(var1, var2, ...);
% [...] = extract_named_args(..., 'VariableNames', {name1, name2, ...});
% [...] = extract_named_args(..., 'RowNames', {name1, name2, ...});
% [...] = extract_named_args(..., 'DimensionNames', {name1, name2, ...});
%
% Used to extract named arguments, ``'VariableNames'``,
% ``'RowNames'``, and ``'DimensionNames'``, to pass to constructor.
var_names = {};
row_names = {};
dim_names = {};
Expand Down
187 changes: 187 additions & 0 deletions lib/mp_table_subclass.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
classdef mp_table_subclass
% mp_table_subclass - Class that acts like a table but isn't one.
%
% Addresses two issues with inheriting from **table** classes (:class:`table`)
% or mp_table).
%
% 1. In MATLAB, :class:`table` is a sealed class, so you cannot inherit
% from it. You can, however, use a subclass of mp_table, but that can
% result in the next issue under Octave.
% 2. While nesting of tables works just fine in general, when using mp_table
% in Octave (at least up through 8.4.0), you cannot nest a subclass of
% mp_table inside another mp_table object because of this bug:
% https://savannah.gnu.org/bugs/index.php?65037.
%
% To work around these issues, your "table subclass" can inherit from **this**
% class. An object of this class **isn't** a :class:`table` or mp_table object,
% but rather it **contains** one and attempts to act like one. That is, it
% delegates method calls (currently only those available in mp_table, listed
% below) to the contained table object.
%
% The class of the contained table object is either :class:`table` or mp_table
% and is determined by mp_table_class.
%
% .. admonition:: Limitation
%
% In MATLAB, when nesting an mp_table_subclass object within another
% mp_table_subclass object, one cannot use multi-level indexing directly.
% E.g. If ``T2`` is a variable in ``T1`` and ``x`` is a variable in ``T2``,
% attempting ``x = T1.T2.x`` will result in an error. The indexing must
% be done in multiple steps ``T2 = T1.T2; x = T2.x``. Note: This only
% applies to MATLAB, where the contained table is a :class:`table`. It works
% just fine in Octave, where the contained table is an :class:`mp_table`.
%
% .. important::
%
% Since the dot syntax ``T.<var_name>`` is used to access table variables,
% you must use a functional syntax ``<method>(T,...)``, as opposed to
% the object-oriented ``T.<method>(...)``, to call methods of this class
% or subclasses, as with mp_table.
%
% mp.mp_table_subclass Properties:
% * tab - *(table or mp_table)* contained table object this class emulates
%
% mp.cost_table Methods:
% * mp_table_subclass - construct object
% * get_table - return the table stored in :attr:`tab`
% * set_table - assign a table to :attr:`tab`
% * istable - true for mp_table objects
% * size - dimensions of table
% * isempty - true if table has no columns or no rows
% * end - used to index last row or variable/column
% * subsref - indexing a table to retrieve data
% * subsasgn - indexing a table to assign data
% * horzcat - concatenate tables horizontally
% * vertcat - concatenate tables vertically
% * display - display table contents
%
% See also mp_table, mp_table_class.

% MATPOWER
% Copyright (c) 2023, Power Systems Engineering Research Center (PSERC)
% by Ray Zimmerman, PSERC Cornell
%
% This file is part of MATPOWER.
% Covered by the 3-clause BSD License (see LICENSE file for details).
% See https://matpower.org for more info.

properties
tab
end %% properties

%% delegate all mp_table methods to obj.tab
methods
function obj = mp_table_subclass(varargin)

args = varargin;
if nargin
%% extract named arguments
[var_names, row_names, dim_names, args] = ...
mp_table.extract_named_args(args);

%% set default variable names
nv = length(args); %% number of variables
if length(var_names) < nv
for k = nv:-1:length(var_names)+1
var_names{k} = inputname(k);
if isempty(var_names{k})
var_names{k} = sprintf('Var%d', k);
end
end
end
args(end+1:end+2) = {'VariableNames', var_names};
if ~isempty(row_names)
args(end+1:end+2) = {'RowNames', row_names};
end
if ~isempty(dim_names)
args(end+1:end+2) = {'DimensionNames', dim_names};
end
end

table_class = mp_table_class();
obj.tab = table_class(args{:});
end

function tab = get_table(obj)
%
% ::
%
% T = get_table(obj)
tab = obj.tab;
end

function obj = set_table(obj, T)
%
% ::
%
% set_table(obj, T)
obj.tab = T;
end

function TorF = istable(obj)
TorF = istable(obj.tab);
end

function varargout = size(obj, varargin)
[varargout{1:nargout}] = size(obj.tab, varargin{:});
end

function TorF = isempty(obj)
TorF = isempty(obj.tab);
end

function N = end(obj, k, n)
N = size(obj.tab, k);
end

function n = numArgumentsFromSubscript(obj, varargin)
n = numArgumentsFromSubscript(obj.tab, varargin{:});
end

function n = numel(obj, varargin)
n = numel(obj.tab, varargin{:});
end

function b = subsref(obj, varargin)
b = subsref(obj.tab, varargin{:});
if varargin{1}(1).type(1) == '(' && ...
(isa(b, 'table') || isa(b, 'mp_table'))
o = feval(class(obj));
b = set_table(o, b);
end
end

function obj = subsasgn(obj, varargin)
for k = 2:length(varargin)
if isa(varargin{k}, 'mp_table_subclass')
varargin{k} = get_table(varargin{k});
end
end
obj.tab = subsasgn(obj.tab, varargin{:});
end

function obj = horzcat(obj, varargin)
args = varargin;
for k = 1:length(args)
if isa(args{k}, 'mp_table_subclass')
args{k} = args{k}.tab;
end
end
obj.tab = horzcat(obj.tab, args{:});
end

function obj = vertcat(obj, varargin)
args = varargin;
for k = 1:length(args)
if isa(args{k}, 'mp_table_subclass')
args{k} = args{k}.tab;
end
end
obj.tab = vertcat(obj.tab, args{:});
end

function display(obj)
obj.tab
end
end %% methods
end %% classdef
Loading

0 comments on commit 346efa6

Please sign in to comment.