سؤال

ما هي أفضل طريقة لتنفيذ التعداد لغة في روبي ؟ أنا أبحث عن شيء الذي أنا يمكن استخدام (تقريبا) مثل Java/C# enums.

هل كانت مفيدة؟

المحلول

اثنين من الطرق.الرموز (: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

نصائح أخرى

أنا مندهش من أن لا أحد قد عرضت شيئا من هذا القبيل ما يلي (المقطوع من رابي جوهرة):

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 أسلوب الثوابت/enums (كما هو الحال عند استخدام 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)

هذا هو قليلا مفتوحة أكثر من enums ، ولكن تناسبها بشكل جيد مع روبي الروح.

الرموز أيضا أداء جيدا للغاية.المقارنة بين اثنين من رموز المساواة ، على سبيل المثال ، هو أسرع بكثير مقارنة سلسلتين.

أنا استخدم الطريقة التالية:

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

أنا أحب ذلك على المزايا التالية:

  1. مجموعات القيم بصريا واحدة كاملة
  2. فإنه تجميع بعض الوقت التدقيق (على النقيض من مجرد استخدام الرموز)
  3. أنا يمكن بسهولة الحصول على قائمة من جميع القيم الممكنة:فقط MY_ENUM
  4. يمكن بسهولة الوصول متميزة القيم: MY_VALUE_1
  5. فإنه يمكن أن يكون القيم من أي نوع ، وليس مجرد رمز ،

رموز قد يكون أفضل لأنك لا تضطر إلى كتابة اسم الخارجي الدرجة, إذا كنت تستخدم فإنه في آخر الصف (MyClass::MY_VALUE_1)

إذا كنت تستخدم القضبان 4.2 أو أعلى يمكنك استخدام القضبان enums.

القضبان الآن enums بشكل افتراضي دون الحاجة بما في ذلك أي الأحجار الكريمة.

هذه هي مشابهة جدا (مع ميزات) إلى Java, C++ enums.

مقتبسة من 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

هذا هو نهج enums في روبي.كنت تسير لفترة قصيرة وحلوة ، وليس بالضرورة الأكثر ج-مثل.أي أفكار ؟

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

تحقق من روبي-التعداد جوهرة ، 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# مثل السلوك.شخصيا ما زلت تعلم روبي ، لقد صدمت قليلا عندما أردت أن تجعل فئة معينة تحتوي على ثابت التعداد ، وربما تجزئة هذا لم يكن بالضبط العثور عليها بسهولة عن طريق جوجل.

كل هذا يعتمد على كيفية استخدام جافا أو C# enums.كيفية استخدامه سوف تملي الحل عليك أن تختار في روبي.

محاولة الأم 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"}>

في الآونة الأخيرة ونحن صدر جوهرة التي تطبق Enums في روبي.في بلدي بعد سوف تجد إجابات على الأسئلة الخاصة بك.كما وصفت هناك لماذا لدينا تنفيذ أفضل من تلك الموجودة (في الواقع هناك العديد من تطبيقات هذه الميزة في روبي بعد الأحجار الكريمة).

الرموز هي روبي الطريق.ومع ذلك ، في بعض الأحيان يحتاج المرء إلى التحدث إلى بعض التعليمات البرمجية C أو شيء أو جافا التي تعرض بعض التعداد لأشياء مختلفة.


#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

وهذا يمكن بالطبع أن يتم مجردة و يمكنك لفة الخاصة بنا التعداد الدرجة

لقد نفذت enums مثل هذا

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

هذا يعطيني الصرامة من c# التعداد وهي مرتبطة نموذج.

حل آخر هو استخدام 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'

معظم الناس استخدام الرموز (هذا هو :foo_bar بناء الجملة).انهم نوع من فريدة من نوعها مبهمة القيم.الرموز لا تنتمي إلى أي enum نوع الطراز حتى انهم لا حقا المؤمنين تمثيل 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 -
2 - ب
3 - ج
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

أحيانا كل ما تحتاجه هو أن تكون قادرة على جلب التعداد قيمة تحديد اسمها مماثلة جافا العالم.

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'

هذا لي يخدم الغرض من التعداد ويحفظه جدا الموسعة أيضا.يمكنك إضافة المزيد من طرق التعداد فئة فيولا الحصول عليها مجانا في كل تعريف enums.على سبيل المثال.get_all_names وأشياء من هذا القبيل.

وثمة نهج آخر هو استخدام روبي الدرجة مع تجزئة تحتوي على أسماء و القيم كما هو موضح في التالي RubyFleebie بلوق وظيفة.هذا يسمح لك بسهولة تحويل بين القيم والثوابت (خاصة إذا قمت بإضافة فئة طريقة البحث عن اسم معين القيمة).

أعتقد أن أفضل طريقة لتنفيذ التعداد مثل أنواع هي مع رموز منذ تقريبا تتصرف مثل عدد صحيح (عندما يتعلق الأمر الفهرسه ، object_id يستخدم لإجراء مقارنات );كنت لا داعي للقلق حول الفهرسة وأنها تبدو أنيق حقا في التعليمات البرمجية الخاصة بك xD

طريقة أخرى لتقليد التعداد يتفق مع المساواة في التعامل مع (دون خجل المعتمدة من ديف توماس).يسمح فتح enums (مثل الكثير من الرموز) و مغلقة (محددة مسبقا) enums.

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

استخدام استخدام التعداد:

method_that_needs_options(FeelsLikeAnEnum.Option_1)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top