Как мне создать перечисляемые типы в MATLAB?

41

Существуют ли перечисляемые типы в MATLAB? Если нет, каковы альтернативы?

Теги:
enums
matlab-class

10 ответов

24
Лучший ответ

Вы можете получить некоторые функции в классах MATLAB нового стиля:

classdef (Sealed) Colors
    properties (Constant)
        RED = 1;
        GREEN = 2;
        BLUE = 3;
    end

    methods (Access = private)    % private so that you cant instantiate
        function out = Colors
        end
    end
end

На самом деле это не тип, но поскольку MATLAB свободно набирается, если вы используете целые числа, вы можете делать то, что его приближает:

line1 = Colors.RED;
...
if Colors.BLUE == line1
end

В этом случае "перечисления" MATLAB близки к перечислениям C-стиля - заменяют синтаксис целых чисел.

При осторожном использовании статических методов вы даже можете сделать переходы MATLAB подходить к Ada в изощренности, но, к сожалению, с неуклюжим синтаксисом.

  • 0
    Просто будьте в курсе возможных падений производительности при использовании нового объектно-ориентированного материала. По моему опыту, это привело к значительным накладным расходам. Однако это действительно зависит от того, что вы делаете.
  • 5
    На самом деле, для простых классов не существует временной потери по сравнению с использованием множества глобальных структур. Тем не менее, экономия времени на разработку передового опыта является существенной, и редко возникает проблема времени выполнения. Оборудование дешевое. Люди, пишущие программное обеспечение, не являются.
40

Начиная с R2010b, MATLAB поддерживает перечисления.

Пример из документация:

classdef Colors
   properties
      R = 0;
      G = 0;
      B = 0;
   end

   methods
      function c = Colors(r, g, b)
         c.R = r; c.G = g; c.B = b;
      end
   end

   enumeration
      Red   (1, 0, 0)
      Green (0, 1, 0)
      Blue  (0, 0, 1)
   end
end
  • 5
    для будущих читателей это, вероятно, должен быть принятый ответ (тем не менее, все хорошие решения)
  • 2
    Это более сложный пример класса enum. Более простой можно найти в документации.
Показать ещё 1 комментарий
14

Если вы хотите сделать что-то похожее на Marc, вы можете просто сделать структуру для представления ваших перечисленных типов вместо целого нового класса:

colors = struct('RED',1,'GREEN',2,'BLUE',3);

Одно из преимуществ заключается в том, что вы можете легко получить доступ к структурам двумя разными способами. Вы можете указать поле напрямую, используя имя поля:

a = colors.RED;

или вы можете использовать имена динамических полей, если у вас есть имя поля в строке:

a = colors.('RED');

По правде говоря, есть несколько преимуществ в том, что Марк предложил и создал целый новый класс для представления объекта enum:

  • Вы можете контролировать, как изменяется объект.
  • Вы можете сохранить определение в одном месте и легко использовать его в нескольких местах.
  • Вы можете управлять сбоями и сделать их более "грациозными", например, возвращать пустую матрицу, если вы пытаетесь получить доступ к несуществующему полю (в отличие от того, чтобы вызывать ошибку).

Однако, если вам не нужна такая сложность и просто нужно что-то сделать быстро, структура, вероятно, самая простая и самая простая реализация. Он также будет работать со старыми версиями MATLAB, которые не используют новейшую структуру OOP.

8

В MATLAB R2009b есть ключевое слово, называемое "enumeration". Кажется, это недокументировано, и я не могу сказать, что знаю, как его использовать, но функциональность, вероятно, есть.

Вы можете найти его в matlabroot\toolbox\distcomp\examples\+examples

classdef(Enumeration) DmatFileMode < int32

    enumeration
        ReadMode(0)
        ReadCompatibilityMode(1)
        WriteMode(2)
    end
<snip>
end
  • 1
    Это правильный способ сделать это, если вы будете выполнять генерацию кода. Это лучше документировано в документации Simulink в разделе «Определение перечисляемого типа данных».
7

Вы также можете использовать классы перечисления Java из вашего кода Matlab. Определите их в Java и поместите их в свой Matlab javaclasspath.

// Java class definition
package test;
public enum ColorEnum {
    RED, GREEN, BLUE
}

Вы можете ссылаться на них по имени в M-коде.

mycolor = test.ColorEnum.RED
if mycolor == test.ColorEnum.RED
    disp('got red');
else
    disp('got other color');
end

% Use ordinal() to get a primitive you can use in a switch statement
switch mycolor.ordinal
    case test.ColorEnum.BLUE.ordinal
        disp('blue');
    otherwise
        disp(sprintf('other color: %s', char(mycolor.toString())))
end

Однако он не будет сравнивать сравнения с другими типами. И сравнение с строкой имеет нечетный размер возврата.

>> test.ColorEnum.RED == 'GREEN'
ans =
     0
>> test.ColorEnum.RED == 'RED'
ans =
     1     1     1
5

Вы можете сделать класс Matlab, который ведет себя как Java old typesafe enum pattern. Модификация Marc solution может взять его из C-style typedefs, чтобы больше напоминать типы имен в стиле Java. В этой версии значения в константах печатаются в цветовых объектах.

Повышения:

  • Тип может быть проверен (во время выполнения) на == и другими операциями, чтобы предотвратить случайное сравнение с необработанными числами или другими типами перечислений.
  • Вы можете явно проверить тип переменных (во время выполнения).
  • Значения отображаются с читаемыми именами вместо непрозрачных кодов.
  • Операции, такие как mean() и std(), которые не имеют смысла в перечислениях, запрещены.

Downsides:

  • Более длинное определение класса. Но это все шаблон и может быть повторно использован для любого другого класса enum, изменяя только имя класса и свойства Constant.
  • Эти перечисления нельзя использовать непосредственно в блоках переключателей. Необходимо вывести код, который теряет определенную безопасность.
  • Объекты будут медленнее, чем примитивы. Релевантно, если вы используете константы внутри циклов.

В целом, я не знаю, какой подход лучше. Не использовались ни на практике.

classdef (Sealed) Color
%COLOR Example of Java-style typesafe enum for Matlab

properties (Constant)
    RED = Color(1, 'RED');
    GREEN = Color(2, 'GREEN');
    BLUE = Color(3, 'BLUE');
end
properties (SetAccess=private)
    % All these properties are immutable.
    Code;
    Name;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
methods (Access = private)
%private so that you can't instatiate directly
    function out = Color(InCode, InName)
        out.Code = InCode;
        out.Name = InName;
    end       
end
methods (Static = true)
    function needa(obj)
    %NEEDA Asserts that obj must be a Color
        if ~isa(obj, mfilename)
            error('Input must be a %s; got a %s', mfilename, class(obj));
        end
    end
end
methods (Access = public)
    function display(obj)
      disp([inputname(1) ' =']);
      disp(obj);
    end
    function disp(obj)
        if isscalar(obj)
            disp(sprintf('%s: %s (%d)', class(obj), obj.Name, obj.Code));
        else
            disp(sprintf('%s array: size %s', class(obj), mat2str(size(obj))));
        end
    end    
    function out = eq(a, b)
        %EQ Basic "type-safe" eq
        check_type_safety(a, b);
        out = [a.Code] == [b.Code];
    end
    function [tf,loc] = ismember(a, b)
        check_type_safety(a, b);
        [tf,loc] = ismember([a.Code], [b.Code]);
    end
    function check_type_safety(varargin)
        %CHECK_TYPE_SAFETY Check that all inputs are of this enum type
        for i = 1:nargin
            if ~isa(varargin{i}, mfilename)
                error('Non-typesafe comparison of %s vs. %s', mfilename, class(varargin{i}));
            end
        end
    end
end
end

Здесь используется функция для ее использования.

function do_stuff_with_color(c)
%DO_STUFF_WITH_COLOR Demo use of the Color typesafe enum

Color.needa(c); % Make sure input was a color
if (c == Color.BLUE)
    disp('color was blue');
else
    disp('color was not blue');
end

% To work with switch statements, you have to explicitly pop the code out 
switch c.Code
    case Color.BLUE.Code
        disp('blue');
    otherwise
        disp(sprintf('some other color: %s', c.Name));
end

Пример использования:

>> Color.RED == Color.RED
ans =
     1
>> Color.RED == 1
??? Error using ==> Color>Color.check_type_safety at 55
Non-typesafe comparison of Color vs. double

Error in ==> Color>Color.eq at 44
        check_type_safety(a, b);

>> do_stuff_with_color(Color.BLUE)
color was blue
blue
>> do_stuff_with_color(Color.GREEN)
color was not blue
some other color: GREEN
>> do_stuff_with_color(1+1) % oops - passing the wrong type, should error
??? Error using ==> Color>Color.needa at 26
Input must be a Color; got a double

Error in ==> do_stuff_with_color at 4
Color.needa(c); % Make sure input was a color

>> 

Небольшая причуда в обоих подходах: соглашение C о размещении константы в левой части "==" для предотвращения плохого присваивания здесь не помогает. В Matlab, если вы случайно используете "=" с этой константой в LHS вместо ошибки, она просто создаст новую локальную структурную переменную с именем Colors и замаскирует класс enum.

>> Colors.BLUE = 42
Colors = 
    BLUE: 42
>> Color.BLUE = 42
Color = 
    BLUE: 42
>> Color.RED
??? Reference to non-existent field 'RED'.
  • 0
    Очень мило, Эндрю. Мне было интересно, когда ты войдешь.
3

После опроса других предложений на этой странице я приземлился на Эндрю полностью объектно-ориентированного подхода. Очень приятно - спасибо Андрею.

В случае, если кто-то заинтересован, однако, я сделал (что я думаю) некоторые улучшения. В частности, я удалил необходимость удвоить имя объекта перечисления. Имена теперь получены с использованием отражения и метаклассической системы. Кроме того, функции eq() и ismember() были перезаписаны, чтобы вернуть возвращаемые правильные значения возвращаемых значений для матриц объектов перечисления. И, наконец, функция check_type_safety() была изменена, чтобы сделать ее совместимой с каталогами пакетов (например, пространствами имен).

Кажется хорошо работать, но дайте мне знать, что вы думаете:

classdef (Sealed) Color
%COLOR Example of Java-style typesafe enum for Matlab

properties (Constant)
    RED = Color(1);
    GREEN = Color(2);
    BLUE = Color(3);
end
methods (Access = private) % private so that you can''t instatiate directly
    function out = Color(InCode)
        out.Code = InCode;
    end       
end


% ============================================================================
% Everything from here down is completely boilerplate - no need to change anything.
% ============================================================================
properties (SetAccess=private) % All these properties are immutable.
    Code;
end
properties (Dependent, SetAccess=private)
    Name;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
methods
    function out = eq(a, b) %EQ Basic "type-safe" eq
        check_type_safety(a, b);
        out = reshape([a.Code],size(a)) == reshape([b.Code],size(b));
    end
    function [tf,loc] = ismember(a, b)
        check_type_safety(a, b);
        [tf,loc] = ismember(reshape([a.Code],size(a)), [b.Code]);
    end
    function check_type_safety(varargin) %CHECK_TYPE_SAFETY Check that all inputs are of this enum type
        theClass = class(varargin{1});
        for ii = 2:nargin
            if ~isa(varargin{ii}, theClass)
                error('Non-typesafe comparison of %s vs. %s', theClass, class(varargin{ii}));
            end
        end
    end

    % Display stuff:
    function display(obj)
        disp([inputname(1) ' =']);
        disp(obj);
    end
    function disp(obj)
        if isscalar(obj)
            fprintf('%s: %s (%d)\n', class(obj), obj.Name, obj.Code);
        else
            fprintf('%s array: size %s\n', class(obj), mat2str(size(obj)));
        end
    end    
    function name=get.Name(obj)
        mc=metaclass(obj);
        mp=mc.Properties;
        for ii=1:length(mp)
            if (mp{ii}.Constant && isequal(obj.(mp{ii}.Name).Code,obj.Code))
                name = mp{ii}.Name;
                return;
            end;
        end;
        error('Unable to find a %s value of %d',class(obj),obj.Code);
    end;
end
end

Спасибо, Мейсон

3

Если у вас есть доступ к панели инструментов статистики, вы можете использовать категориальный объект.

2
Toys = {'Buzz', 'Woody', 'Rex', 'Hamm'};

Toys{3}
  ans = 'Rex'
1

Если вам нужны перечисленные типы только для перехода на сборку С# или .NET, вы можете создавать и передавать перечисления с помощью MATLAB 2010:

A = NET.addAssembly(MyName.dll)
% suppose you have enum called "MyAlerts" in your assembly
myvar = MyName.MyAlerts.('value_1');

вы также можете проверить официальный ответ MathWorks на

Как использовать перечисляемые значения .NET в MATLAB 7.8 (R2009a)?

// the enum "MyAlerts" in c# will look something like this
public enum MyAlerts
{
    value_1 = 0,
    value_2 = 1,
    MyAlerts_Count = 2,
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню