Programing

루비에서 열거 형을 구현하는 방법은 무엇입니까?

lottogame 2020. 3. 11. 00:42
반응형

루비에서 열거 형을 구현하는 방법은 무엇입니까?


루비에서 열거 형 관용구를 구현하는 가장 좋은 방법은 무엇입니까? Java / C # 열거 형과 같이 (거의) 사용할 수있는 것을 찾고 있습니다.


두 가지 방법. 기호 ( :foo표기법) 또는 상수 ( FOO표기법).

리터럴 문자열로 코드를 흩 뜨리지 않고 가독성을 높이려면 기호가 적합합니다.

postal_code[:minnesota] = "MN"
postal_code[:new_york] = "NY"

상수는 중요한 기본 값이있을 때 적합합니다. 상수를 보유 할 모듈을 선언 한 다음 그 안에 상수를 선언하십시오.

module Foo
  BAR = 1
  BAZ = 2
  BIZ = 4
end

flags = Foo::BAR | Foo::BAZ # flags = 3

아무도 다음과 같은 것을 제공하지 않았다는 것에 놀랐습니다 ( RAPI gem 에서 수확 ).

class Enum

  private

  def self.enum_attr(name, num)
    name = name.to_s

    define_method(name + '?') do
      @attrs & num != 0
    end

    define_method(name + '=') do |set|
      if set
        @attrs |= num
      else
        @attrs &= ~num
      end
    end
  end

  public

  def initialize(attrs = 0)
    @attrs = attrs
  end

  def to_i
    @attrs
  end
end

다음과 같이 사용할 수 있습니다.

class FileAttributes < Enum
  enum_attr :readonly,       0x0001
  enum_attr :hidden,         0x0002
  enum_attr :system,         0x0004
  enum_attr :directory,      0x0010
  enum_attr :archive,        0x0020
  enum_attr :in_rom,         0x0040
  enum_attr :normal,         0x0080
  enum_attr :temporary,      0x0100
  enum_attr :sparse,         0x0200
  enum_attr :reparse_point,  0x0400
  enum_attr :compressed,     0x0800
  enum_attr :rom_module,     0x2000
end

예:

>> example = FileAttributes.new(3)
=> #<FileAttributes:0x629d90 @attrs=3>
>> example.readonly?
=> true
>> example.hidden?
=> true
>> example.system?
=> false
>> example.system = true
=> true
>> example.system?
=> true
>> example.to_i
=> 7

이것은 데이터베이스 시나리오에서 또는 C 스타일 상수 / 열을 처리 할 때 ( RAPI가 광범위하게 사용하는 FFI를 사용할 때와 같이) 잘 작동합니다.

또한 해시 유형 솔루션을 사용할 때와 마찬가지로 오실로 인한 오류 발생에 대해 걱정할 필요가 없습니다.


이를 수행하는 가장 관용적 인 방법은 기호를 사용하는 것입니다. 예를 들어,

enum {
  FOO,
  BAR,
  BAZ
}

myFunc(FOO);

... 기호를 사용할 수 있습니다.

# You don't actually need to declare these, of course--this is
# just to show you what symbols look like.
:foo
:bar
:baz

my_func(:foo)

이것은 열거 형보다 약간 개방형이지만 Ruby 정신에 잘 맞습니다.

기호도 매우 잘 작동합니다. 예를 들어, 두 기호가 동일한 지 비교하는 것은 두 문자열을 비교하는 것보다 훨씬 빠릅니다.


다음과 같은 접근 방식을 사용합니다.

class MyClass
  MY_ENUM = [MY_VALUE_1 = 'value1', MY_VALUE_2 = 'value2']
end

다음과 같은 장점이 있습니다.

  1. 시각적으로 하나의 값으로 그룹화합니다.
  2. 컴파일 타임 검사를 수행합니다 (심볼을 사용하는 것과 대조적으로)
  3. 가능한 모든 값 목록에 쉽게 액세스 할 수 있습니다. MY_ENUM
  4. 고유 한 값에 쉽게 액세스 할 수 있습니다. MY_VALUE_1
  5. Symbol뿐만 아니라 모든 유형의 값을 가질 수 있습니다.

다른 클래스 ( MyClass::MY_VALUE_1) 에서 외부 클래스 이름을 사용하는 경우 기호를 사용하면 외부 클래스 이름을 쓰지 않아도됩니다.


Rails 4.2 이상을 사용하는 경우 Rails 열거 형을 사용할 수 있습니다.

Rails에는 보석을 포함 할 필요없이 기본적으로 열거 형이 있습니다.

이것은 Java, C ++ 열거 형과 매우 유사합니다.

http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html 에서 인용 :

class Conversation < ActiveRecord::Base
  enum status: [ :active, :archived ]
end

# conversation.update! status: 0
conversation.active!
conversation.active? # => true
conversation.status  # => "active"

# conversation.update! status: 1
conversation.archived!
conversation.archived? # => true
conversation.status    # => "archived"

# conversation.update! status: 1
conversation.status = "archived"

# conversation.update! status: nil
conversation.status = nil
conversation.status.nil? # => true
conversation.status      # => nil

이것이 Ruby의 열거 형에 대한 나의 접근 방식입니다. 나는 가장 C 같은 것은 아니지만 짧고 달콤하게 가고 있었다. 이견있는 사람?

module Kernel
  def enum(values)
    Module.new do |mod|
      values.each_with_index{ |v,i| mod.const_set(v.to_s.capitalize, 2**i) }

      def mod.inspect
        "#{self.name} {#{self.constants.join(', ')}}"
      end
    end
  end
end

States = enum %w(Draft Published Trashed)
=> States {Draft, Published, Trashed} 

States::Draft
=> 1

States::Published
=> 2

States::Trashed
=> 4

States::Draft | States::Trashed
=> 3

ruby-enum gem https://github.com/dblock/ruby-enum을 확인하십시오 .

class Gender
  include Enum

  Gender.define :MALE, "male"
  Gender.define :FEMALE, "female"
end

Gender.all
Gender::MALE

나는 그 사람 이이 질문을 게시 한 지 오랜 시간이 걸렸다는 것을 알고 있지만 같은 질문이 있었고이 게시물은 나에게 답을주지 못했습니다. 열거 형을 나타내는 열을 사용하여 숫자가 나타내는 것, 쉬운 비교 및 ​​조회를위한 모든 ActiveRecord 지원의 대부분을 볼 수있는 쉬운 방법을 원했습니다.

나는 아무것도 찾지 못했기 때문에 찾고있는 모든 것을 허용하는 yinum 이라는 멋진 구현을 만들었습니다 . 많은 사양으로 만들어 졌으므로 안전하다고 확신합니다.

일부 기능 예 :

COLORS = Enum.new(:COLORS, :red => 1, :green => 2, :blue => 3)
=> COLORS(:red => 1, :green => 2, :blue => 3)
COLORS.red == 1 && COLORS.red == :red
=> true

class Car < ActiveRecord::Base    
  attr_enum :color, :COLORS, :red => 1, :black => 2
end
car = Car.new
car.color = :red / "red" / 1 / "1"
car.color
=> Car::COLORS.red
car.color.black?
=> false
Car.red.to_sql
=> "SELECT `cars`.* FROM `cars` WHERE `cars`.`color` = 1"
Car.last.red?
=> true

기호가있는 오타가 걱정되는 경우 존재하지 않는 키로 값에 액세스 할 때 코드에서 예외가 발생하는지 확인하십시오. fetch대신 다음을 사용하여이 작업을 수행 할 수 있습니다 [].

my_value = my_hash.fetch(:key)

또는 존재하지 않는 키를 제공하는 경우 기본적으로 해시에서 예외를 발생시킵니다.

my_hash = Hash.new do |hash, key|
  raise "You tried to access using #{key.inspect} when the only keys we have are #{hash.keys.inspect}"
end

해시가 이미 존재하면 예외 발생 동작을 추가 할 수 있습니다.

my_hash = Hash[[[1,2]]]
my_hash.default_proc = proc do |hash, key|
  raise "You tried to access using #{key.inspect} when the only keys we have are #{hash.keys.inspect}"
end

일반적으로 상수가있는 오타 안전에 대해 걱정할 필요가 없습니다. 상수 이름을 잘못 입력하면 일반적으로 예외가 발생합니다.


아마도 가장 가벼운 방법은

module MyConstants
  ABC = Class.new
  DEF = Class.new
  GHI = Class.new
end

이 방법으로 값은 Java / C #에서와 같이 연관된 이름을 갖습니다.

MyConstants::ABC
=> MyConstants::ABC

모든 값을 얻으려면 다음을 수행하십시오.

MyConstants.constants
=> [:ABC, :DEF, :GHI] 

열거 형의 서수 값을 원한다면 할 수 있습니다

MyConstants.constants.index :GHI
=> 2

누군가가 계속해서 Renum 이라는 루비 보석을 썼습니다 . 가장 가까운 Java / C # 같은 동작을 얻는다고 주장합니다. 개인적으로 나는 루비를 배우고 있는데 특정 클래스에 정적 열거 형, 아마도 해시가 포함되어 Google을 통해 쉽게 찾을 수없는 해시를 만들고 싶을 때 약간 충격을 받았습니다.


그것은 모두 Java 또는 C # 열거 형을 사용하는 방법에 달려 있습니다. 사용 방법에 따라 Ruby에서 선택할 솔루션이 결정됩니다.

기본 Set유형을 사용해보십시오 .

>> enum = Set['a', 'b', 'c']
=> #<Set: {"a", "b", "c"}>
>> enum.member? "b"
=> true
>> enum.member? "d"
=> false
>> enum.add? "b"
=> nil
>> enum.add? "d"
=> #<Set: {"a", "b", "c", "d"}>

최근에 우리 는 Ruby에서 Enums 를 구현 하는 gem출시했습니다 . 게시물 에는 질문에 대한 답변이 있습니다. 또한 구현이 기존 구현보다 나은 이유를 설명했습니다 (실제로 Ruby에는 보석 으로이 기능의 구현이 많이 있습니다).


다른 솔루션은 OpenStruct를 사용하는 것입니다. 꽤 똑 바르고 깨끗합니다.

https://ruby-doc.org/stdlib-2.3.1/libdoc/ostruct/rdoc/OpenStruct.html

예:

# bar.rb
require 'ostruct' # not needed when using Rails

# by patching Array you have a simple way of creating a ENUM-style
class Array
   def to_enum(base=0)
      OpenStruct.new(map.with_index(base).to_h)
   end
end

class Bar

    MY_ENUM = OpenStruct.new(ONE: 1, TWO: 2, THREE: 3)
    MY_ENUM2 = %w[ONE TWO THREE].to_enum

    def use_enum (value)
        case value
        when MY_ENUM.ONE
            puts "Hello, this is ENUM 1"
        when MY_ENUM.TWO
            puts "Hello, this is ENUM 2"
        when MY_ENUM.THREE
            puts "Hello, this is ENUM 3"
        else
            puts "#{value} not found in ENUM"
        end
    end

end

# usage
foo = Bar.new    
foo.use_enum 1
foo.use_enum 2
foo.use_enum 9


# put this code in a file 'bar.rb', start IRB and type: load 'bar.rb'

기호는 루비 방식입니다. 그러나 때로는 여러 가지에 대한 열거 형을 노출시키는 일부 C 코드 또는 무언가 또는 Java와 대화해야합니다.


#server_roles.rb
module EnumLike

  def EnumLike.server_role
    server_Symb=[ :SERVER_CLOUD, :SERVER_DESKTOP, :SERVER_WORKSTATION]
    server_Enum=Hash.new
    i=0
    server_Symb.each{ |e| server_Enum[e]=i; i +=1}
    return server_Symb,server_Enum
  end

end

이것은 다음과 같이 사용될 수 있습니다


require 'server_roles'

sSymb, sEnum =EnumLike.server_role()

foreignvec[sEnum[:SERVER_WORKSTATION]]=8

이것은 물론 추상적이 될 수 있으며 우리 자신의 Enum 클래스를 굴릴 수 있습니다.


나는 그런 열거 형을 구현했다

module EnumType

  def self.find_by_id id
    if id.instance_of? String
      id = id.to_i
    end 
    values.each do |type|
      if id == type.id
        return type
      end
    end
    nil
  end

  def self.values
    [@ENUM_1, @ENUM_2] 
  end

  class Enum
    attr_reader :id, :label

    def initialize id, label
      @id = id
      @label = label
    end
  end

  @ENUM_1 = Enum.new(1, "first")
  @ENUM_2 = Enum.new(2, "second")

end

작업하기 쉬운

EnumType.ENUM_1.label

...

enum = EnumType.find_by_id 1

...

valueArray = EnumType.values

이것은 약간 불필요한 것처럼 보이지만 이것은 xml 또는 일부와 통합 할 때 특히 몇 번 사용했던 방법입니다.

#model
class Profession
  def self.pro_enum
    {:BAKER => 0, 
     :MANAGER => 1, 
     :FIREMAN => 2, 
     :DEV => 3, 
     :VAL => ["BAKER", "MANAGER", "FIREMAN", "DEV"]
    }
  end
end

Profession.pro_enum[:DEV]      #=>3
Profession.pro_enum[:VAL][1]   #=>MANAGER

이것은 나에게 ac # enum의 엄격함을 제공하며 모델과 관련이 있습니다.


대부분의 사람들은 기호를 사용합니다 ( :foo_bar구문입니다). 그것들은 일종의 독특한 불투명 값입니다. 기호는 열거 형 스타일에 속하지 않으므로 실제로 C의 열거 형을 충실하게 표현하지는 않지만 얻을 수있는만큼 좋습니다.


irb(main):016:0> num=[1,2,3,4]
irb(main):017:0> alph=['a','b','c','d']
irb(main):018:0> l_enum=alph.to_enum
irb(main):019:0> s_enum=num.to_enum
irb(main):020:0> loop do
irb(main):021:1* puts "#{s_enum.next} - #{l_enum.next}"
irb(main):022:1> end

산출:

1-a
2-b
3-c
4-d


module Status
  BAD  = 13
  GOOD = 24

  def self.to_str(status)
    for sym in self.constants
      if self.const_get(sym) == status
        return sym.to_s
      end
    end
  end

end


mystatus = Status::GOOD

puts Status::to_str(mystatus)

산출:

GOOD

때로는 필요한 것은 열거 형 값을 가져 와서 Java 세계와 비슷한 이름을 식별하는 것입니다.

module Enum
     def get_value(str)
       const_get(str)
     end
     def get_name(sym)
       sym.to_s.upcase
     end
 end

 class Fruits
   include Enum
   APPLE = "Delicious"
   MANGO = "Sweet"
 end

 Fruits.get_value('APPLE') #'Delicious'
 Fruits.get_value('MANGO') # 'Sweet'

 Fruits.get_name(:apple) # 'APPLE'
 Fruits.get_name(:mango) # 'MANGO'

이것은 나에게 열거의 목적을 제공하고 매우 확장 가능하게 유지합니다. Enum 클래스에 더 많은 메소드를 추가하고 정의 된 모든 enum에서 비올라를 무료로 얻을 수 있습니다. 예를 들어. get_all_names와 같은 것들.


또 다른 방법은 다음 RubyFleebie 블로그 게시물에 설명 된대로 이름과 값을 포함하는 해시와 함께 Ruby 클래스를 사용하는 것 입니다. 이를 통해 값과 상수 사이를 쉽게 변환 할 수 있습니다 (특히 주어진 값의 이름을 조회하기 위해 클래스 메소드를 추가하는 경우).


타입과 같은 열거 형을 구현하는 가장 좋은 방법은 기호로 수행하는 것입니다. 왜냐하면 거의 정수처럼 동작하기 때문입니다 (성능에 관해서는 object_id가 비교에 사용됩니다). 인덱싱에 대해 걱정할 필요가 없으며 코드 xD에서 깔끔하게 보입니다.


일관된 평등 처리 (Dave Thomas에서 뻔뻔스럽게 채택)로 열거 형을 모방하는 또 다른 방법. 열린 열거 형 (기호와 유사) 및 닫힌 (사전 정의 된) 열거 형을 허용합니다.

class Enum
  def self.new(values = nil)
    enum = Class.new do
      unless values
        def self.const_missing(name)
          const_set(name, new(name))
        end
      end

      def initialize(name)
        @enum_name = name
      end

      def to_s
        "#{self.class}::#@enum_name"
      end
    end

    if values
      enum.instance_eval do
        values.each { |e| const_set(e, enum.new(e)) }
      end
    end

    enum
  end
end

Genre = Enum.new %w(Gothic Metal) # creates closed enum
Architecture = Enum.new           # creates open enum

Genre::Gothic == Genre::Gothic        # => true
Genre::Gothic != Architecture::Gothic # => true

inum을 사용해보십시오. https://github.com/alfa-jpn/inum

class Color < Inum::Base
  define :RED
  define :GREEN
  define :BLUE
end
Color::RED 
Color.parse('blue') # => Color::BLUE
Color.parse(2)      # => Color::GREEN

https://github.com/alfa-jpn/inum#usage 참조


빠르고 더러운 C #과 같은 느낌 :

class FeelsLikeAnEnum
  def self.Option_1() :option_1 end
  def self.Option_2() :option_2 end
  def self.Option_3() :option_3 end
end

Enum을 사용하는 것처럼 사용하십시오.

method_that_needs_options(FeelsLikeAnEnum.Option_1)

참고 URL : https://stackoverflow.com/questions/75759/how-to-implement-enums-in-ruby

반응형