How do I create enumerated types in MATLAB?

前端 未结 10 2130
不知归路
不知归路 2020-12-02 17:04

Are there enumerated types in MATLAB? If not, what are the alternatives?

相关标签:
10条回答
  • 2020-12-02 17:17

    You could make a Matlab class that behaves like a Java's old typesafe enum pattern. A modification of Marc's solution could take it from C-style typedefs to more like Java-style typesafe enums. In this version, the values in the constants are typed Color objects.

    The upsides:

    • The type can be checked (at runtime) by == and other operations to prevent accidental comparison to raw numerics or other types of enums.
    • You can explicitly check the type of your variables (at runtime).
    • Values are displayed with readable names instead of the opaque codes.
    • Operations like mean() and std() that don't make sense on enums are disallowed.

    Downsides:

    • Longer class definition. But, this is all boilerplate, and can be reused for any other enum class, changing just the class name and Constant properties.
    • These enums cannot be used directly in switch blocks. Need to pop the Code out, which loses some type safety.
    • Objects will be slower than primitives. Relevant if you're using constants inside loops.

    On the whole, I don't know which approach is better. Haven't used either in practice.

    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
    

    Here's a function to exercise it.

    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
    

    Example of use:

    >> 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
    
    >> 
    

    A minor quirk in both approaches: the C convention of putting the constant on the left hand of the "==" to prevent bad assignment doesn't help as much here. In Matlab, if you accidentally use "=" with this constant on the LHS, instead of an error, it'll just create a new local struct variable named Colors, and it will mask the enum class.

    >> Colors.BLUE = 42
    Colors = 
        BLUE: 42
    >> Color.BLUE = 42
    Color = 
        BLUE: 42
    >> Color.RED
    ??? Reference to non-existent field 'RED'.
    
    0 讨论(0)
  • 2020-12-02 17:17
    Toys = {'Buzz', 'Woody', 'Rex', 'Hamm'};
    
    Toys{3}
      ans = 'Rex'
    
    0 讨论(0)
  • 2020-12-02 17:18

    Starting from R2010b, MATLAB supports enumerations.

    Example from the documentation:

    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
    
    0 讨论(0)
  • 2020-12-02 17:24

    You can get some of the functionality with new-style MATLAB classes:

    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
    

    This isn't really a type, but since MATLAB is loosely typed, if you use integers, you can do things that approximate it:

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

    In this case, MATLAB "enums" are close to C-style enums - substitute syntax for integers.

    With the careful use of static methods, you can even make MATLAB enums approach Ada's in sophistication, but unfortunately with clumsier syntax.

    0 讨论(0)
  • 2020-12-02 17:25

    If you have access to the Statistics Toolbox, you might consider using a categorical object.

    0 讨论(0)
  • 2020-12-02 17:27

    If you want to do something similar to what Marc suggested, you could simply make a structure to represent your enumerated types instead of a whole new class:

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

    One benefit is that you can easily access structures in two different ways. You can specify a field directly using the field name:

    a = colors.RED;
    

    or you can use dynamic field names if you have the field name in a string:

    a = colors.('RED');
    

    In truth, there are a few benefits to doing what Marc suggested and creating a whole new class to represent an "enum" object:

    • You can control how the object is modified.
    • You can keep the definition in one place and easily use it in multiple places.
    • You can control failures and make them more "graceful", like returning an empty matrix if you try to access a non-existent field (as opposed to throwing an error).

    However, if you don't need that sort of complexity and just need to do something quick, a structure is likely the easiest and most straight-forward implementation. It will also work with older versions of MATLAB that don't use the newest OOP framework.

    0 讨论(0)
提交回复
热议问题