Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

enum collection of constant values

4 views
Skip to first unread message

Garance A Drosehn

unread,
Aug 2, 2005, 12:14:35 PM8/2/05
to
I'd like to have a set of constants for a class, where their values
need to be unique, and preferably compact. For instance:
SB_NONE = 0; SB_GOOD = 1; SB_BAD = 2; SB_LOGIC_ERR = 3;
SB_MAX = SB_LOGIC_ERR

but if I want to modify this list by adding something in the middle,
then I have to remember to increment (by hand) all the values
which come after that new value.

In C, I would do something like:
typedef enum { SB_NONE, SB_GOOD, SB_BAD,
SB_LOGICERR } sb_set;

Looking through the pickaxe book, it looks like the closest thing
I can do is to create an array of words, and then use the indexes
of where those words are in the array. But that isn't really the
same thing. I'd like the efficiency of having constant values,
which would be referred to by name.

(And after I have that for a compact set of values, I'd want a
similar feature which would give me a bit-wise unique set of
values. Eg: SB_MODA = 1; SB_MODB = 2; SB_MODC = 4;
SB_MODD = 8; -- such that I could combine the values together
using '|', and tell which values were 'or'-ed together to get that
combined value...)

--
Garance Alistair Drosehn = dro...@gmail.com
Senior Systems Programmer or g...@FreeBSD.org
Rensselaer Polytechnic Institute; Troy, NY; USA


Jim Menard

unread,
Aug 2, 2005, 12:29:30 PM8/2/05
to
Garance A Drosehn wrote:
> I'd like to have a set of constants for a class, where their values
> need to be unique, and preferably compact. For instance:
> SB_NONE = 0; SB_GOOD = 1; SB_BAD = 2; SB_LOGIC_ERR = 3;
> SB_MAX = SB_LOGIC_ERR
>
> but if I want to modify this list by adding something in the middle,
> then I have to remember to increment (by hand) all the values
> which come after that new value.
>
> In C, I would do something like:
> typedef enum { SB_NONE, SB_GOOD, SB_BAD,
> SB_LOGICERR } sb_set;
>
> Looking through the pickaxe book, it looks like the closest thing
> I can do is to create an array of words, and then use the indexes
> of where those words are in the array. But that isn't really the
> same thing. I'd like the efficiency of having constant values,
> which would be referred to by name.
>
> (And after I have that for a compact set of values, I'd want a
> similar feature which would give me a bit-wise unique set of
> values. Eg: SB_MODA = 1; SB_MODB = 2; SB_MODC = 4;
> SB_MODD = 8; -- such that I could combine the values together
> using '|', and tell which values were 'or'-ed together to get that
> combined value...)

Use symbols. They are similar to strings, but every use of the same symbol
uses the same value, and they are very compact.

state = :none
state = :good if all_is_well
# ...
state = :logic_error

Jim
--
Jim Menard, ji...@io.com, http://www.io.com/~jimm
"A language that doesn't affect the way you think about programming, is not
worth knowing." -- Alan J. Perlis


Pit Capitain

unread,
Aug 2, 2005, 12:35:52 PM8/2/05
to
Jim Menard schrieb:

.. and for a set of values use the class Set.

Regards,
Pit


Patrick Hurley

unread,
Aug 2, 2005, 12:41:26 PM8/2/05
to
Not quite perfect, but not too bad you can:

SB_MODA, SB_MODB, SB_MODC = *0..2

If your range at the end "goes too far" there is no problem, but if it
comes up short then some of your constants will silently not be
initialized.

Jim Menard

unread,
Aug 2, 2005, 12:49:43 PM8/2/05
to
Pit Capitain wrote:
> Jim Menard schrieb:

>>Use symbols. They are similar to strings, but every use of the same
>>symbol uses the same value, and they are very compact.
>>
>>state = :none
>>state = :good if all_is_well
>># ...
>>state = :logic_error
>
>
> ... and for a set of values use the class Set.

I'm not sure I understand why you suggest the use of Set. Am I missing an
advantage over the user of symbols?

The original poster was looking for the Ruby equivalent of C's enums, which
are essentially a group of named constants. A Set---as implemented in
Set.rb---contains a Hash whose keys are the elements of the set. Though this
would work as intended, it is more work and overhead than necessary. The OP
wanted something that was "preferably compact".

Ara.T.Howard

unread,
Aug 2, 2005, 12:50:26 PM8/2/05
to

class methods are your friend

harp:~ > cat a.rb
module Enum
def self::included other
other.module_eval do
def self::enum(*list)
list.flatten.each_with_index{|e, i| const_set e.to_s.intern, i}
end
def self::bit_enum(*list)
list.flatten.each_with_index{|e, i| const_set e.to_s.intern, 2 ** i}
end
end
end
end

class C
include Enum

enum %w(
FOO
BAR
FOOBAR
BARFOO
)
end

class B
include Enum

bit_enum %w(
FOO
BAR
FOOBAR
BARFOO
)
end

p C::FOO
p C::BARFOO

printf "0b%8.8b\n", B::FOO
printf "0b%8.8b\n", B::BARFOO


harp:~ > ruby a.rb
0
3
0b00000001
0b00001000


cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================

Brian Schröder

unread,
Aug 2, 2005, 12:53:47 PM8/2/05
to

Metaprogramming is your friend:

bschroed@black:~/svn/projekte/ruby-things$ cat enum.rb
class Object
def self.enums(*args)
args.flatten.each_with_index do | const, i |
class_eval %(#{const} = #{i})
end
end

def self.bitwise_enums(*args)
args.flatten.each_with_index do | const, i |
class_eval %(#{const} = #{2**i})
end
end
end


class Foo
enums %w(FOO BAR BAZ)
bitwise_enums %w(ONE TWO FOUR EIGHT)
end

p [Foo::FOO, Foo::BAR, Foo::BAZ]
p [Foo::ONE, Foo::TWO, Foo::FOUR, Foo::EIGHT]
bschroed@black:~/svn/projekte/ruby-things$ ruby enum.rb
[0, 1, 2]
[1, 2, 4, 8]

regards,

Brian

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/


Patrick Hurley

unread,
Aug 2, 2005, 12:57:00 PM8/2/05
to
Oops I had not read your whole message. To handle the binary one, I
think you would need to drop into some real code:

module Kernel

def enum(*syms)
index = 0
puts self
puts self.send(:binding)
syms.each do |sym|
eval "#{sym.to_s} = #{index}"
index += 1
end
end

def binary_enum(*syms)
index = 1
syms.each do |sym|
eval "#{sym.to_s} = #{index}"
index *= 2
end
end

end

enum :A, :B, :C
binary_enum :X, :Y, :Z

would work, but they will always be at the global context. In rite,
will we get a way to get the caller's binding? That would be very nice
to make this method more versatile.

pth

Patrick Hurley

unread,
Aug 2, 2005, 12:58:53 PM8/2/05
to
Oh yeah you can take out my puts statements too :-)

Yohanes Santoso

unread,
Aug 2, 2005, 12:58:28 PM8/2/05
to
Garance A Drosehn <dro...@gmail.com> writes:

> I'd like to have a set of constants for a class, where their values
> need to be unique, and preferably compact. For instance:
> SB_NONE = 0; SB_GOOD = 1; SB_BAD = 2; SB_LOGIC_ERR = 3;
> SB_MAX = SB_LOGIC_ERR
>
> but if I want to modify this list by adding something in the middle,
> then I have to remember to increment (by hand) all the values
> which come after that new value.
>
> In C, I would do something like:
> typedef enum { SB_NONE, SB_GOOD, SB_BAD,
> SB_LOGICERR } sb_set;

SB_ENUM = {:none => 0, :good => 1, :bad => 2, :logic_err => 4}.freeze
SB_MAX = SB_ENUM.values.max

value = SB_ENUM[:bad] | SB_ENUM[:logic_err]

p SB_ENUM[:invalid_enum_value] # ==> nil


YS.

Pit Capitain

unread,
Aug 2, 2005, 1:24:31 PM8/2/05
to
Jim Menard schrieb:

> Pit Capitain wrote:
>
>> Jim Menard schrieb:
>>
>>> Use symbols. They are similar to strings, but every use of the same
>>> symbol uses the same value, and they are very compact.
>>>
>>> state = :none
>>> state = :good if all_is_well
>>> # ...
>>> state = :logic_error
>>
>> ... and for a set of values use the class Set.
>
> I'm not sure I understand why you suggest the use of Set. Am I missing
> an advantage over the user of symbols?

I'm sorry that you didn't understand me. I also think that having
numbers behind the names isn't necessary at all and maybe even
misleading, so I also would suggest to use symbols.

But if you read the original message, Garance wanted to be able to build
sets of his constants and combine the sets with an "or" operator. For
this I suggested use sets (of symbols).

Regards,
Pit


Jim Menard

unread,
Aug 2, 2005, 1:36:17 PM8/2/05
to
Pit Capitain wrote:

> But if you read the original message, Garance wanted to be able to build
> sets of his constants and combine the sets with an "or" operator. For
> this I suggested use sets (of symbols).

That makes perfect sense. Thank you.

Patrick Hurley

unread,
Aug 2, 2005, 3:32:22 PM8/2/05
to
On 8/2/05, Ara.T.Howard <Ara.T....@noaa.gov> wrote:
> On Wed, 3 Aug 2005, Garance A Drosehn wrote:
> > I'd like to have a set of constants for a class, where their values
> > need to be unique, and preferably compact. For instance:
> class methods are your friend
>
> snip nice Enum module

I like the Enum module, but if I include it into the 'main' scope, I need to

self.class.enum to make it work. A bit more brute force, but works
"automatically" in all contexts would be overloading Object. This then
requires a smarter method of setting the const based upon where it is
called from.

class Object

def const_setter
set = if self.respond_to? :const_set
lambda { |sym, val| self.const_set sym.to_s.intern, val }
elsif self.class.respond_to? :const_set
lambda { |sym, val| self.class.const_set sym.to_s.intern, val }
else
# I don't think this code would ever get called...
lambda { |sym, val| eval "#{sym} = #{val}" }
end
end

def enum(*syms)
set = const_setter
syms.each_with_index do |sym,index|
set.call sym, index
end
end

def binary_enum(*syms)
set = const_setter
syms.each_with_index do |sym,index|
set.call sym, 2 ** index
end
end

end


Ara.T.Howard

unread,
Aug 2, 2005, 3:51:45 PM8/2/05
to

hmm. this is a bit shorter


harp:~ > cat a.rb
class Object
def enum(*list)
mc = Module === self ? self : self.class
list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, i}
end
def bit_enum(*list)
mc = Module === self ? self : self.class
list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, 2 ** i}
end
end

class C


enum %w(
FOO
BAR
FOOBAR
BARFOO
)
end

class B


bit_enum %w(
FOO
BAR
FOOBAR
BARFOO
)
end

enum %w(
FOO
BAR
)

bit_enum %w(
FOOBAR
BARFOO
)

p C::FOO
p C::BARFOO
printf "0b%8.8b\n", B::FOO
printf "0b%8.8b\n", B::BARFOO

p FOO
p BAR
printf "0b%8.8b\n", FOOBAR
printf "0b%8.8b\n", BARFOO

harp:~ > ruby a.rb
0
3
0b00000001
0b00001000

0
1
0b00000001
0b00000010


probably could also combine this idea with the Enum module...

nice mod btw.

Joel VanderWerf

unread,
Aug 2, 2005, 4:17:59 PM8/2/05
to
Ara.T.Howard wrote:
> class Object
> def enum(*list)
> mc = Module === self ? self : self.class
> list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, i}
> end
> def bit_enum(*list)
> mc = Module === self ? self : self.class
> list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, 2
> ** i}
> end
> end

Why would you want the mc = self.class option ?

irb(main):022:0> x=[]
=> []
irb(main):023:0> x.instance_eval { enum %w{ZZZ AAA} }
=> ["ZZZ", "AAA"]
irb(main):024:0> ZZZ
NameError: uninitialized constant ZZZ
from (irb):24
irb(main):025:0> Array::ZZZ
=> 0

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407


Ara.T.Howard

unread,
Aug 2, 2005, 4:39:00 PM8/2/05
to
On Wed, 3 Aug 2005, Joel VanderWerf wrote:

> Ara.T.Howard wrote:
>> class Object
>> def enum(*list)
>> mc = Module === self ? self : self.class
>> list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, i}
>> end
>> def bit_enum(*list)
>> mc = Module === self ? self : self.class
>> list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, 2
>> ** i}
>> end
>> end
>
> Why would you want the mc = self.class option ?
>
> irb(main):022:0> x=[]
> => []
> irb(main):023:0> x.instance_eval { enum %w{ZZZ AAA} }
> => ["ZZZ", "AAA"]
> irb(main):024:0> ZZZ
> NameError: uninitialized constant ZZZ
> from (irb):24
> irb(main):025:0> Array::ZZZ
> => 0

isn't this what the op wanted?

harp:~ > cat a.rb
def try
begin
yield
rescue => e
STDERR.puts "#{ e.message } (#{ e.class })"
end
end

class Object
def enum(*list)
mc = self


list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, i}
end
def bit_enum(*list)

mc = self


list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, 2 ** i}
end
end

try do


enum %w(
FOO
BAR
)

p FOO
end

class Object
def enum(*list)
mc = Module === self ? self : self.class
list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, i}
end
def bit_enum(*list)
mc = Module === self ? self : self.class
list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, 2 ** i}
end
end

try do


enum %w(
FOO
BAR
)

p FOO
end


harp:~ > ruby a.rb
undefined method `const_set' for main:Object (NoMethodError)
0

the 'mc = self.class' is when 'self == main'. maybe i misunderstood?

regards.

Garance A Drosehn

unread,
Aug 2, 2005, 9:11:04 PM8/2/05
to
On 8/2/05, Ara.T.Howard <Ara.T....@noaa.gov> wrote:
>
> hmm. this is a bit shorter
>
> harp:~ > cat a.rb

> class Object
> def enum(*list)
> mc = Module === self ? self : self.class
> list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, i}
> end
> def bit_enum(*list)
> mc = Module === self ? self : self.class
> list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, 2 ** i}
> end
> end

Wow. Very nice. Works great in my script. Thanks!

Garance A Drosehn

unread,
Aug 2, 2005, 9:49:04 PM8/2/05
to
On 8/2/05, Ara.T.Howard <Ara.T....@noaa.gov> wrote:

> hmm. this is a bit shorter

> class Object


> def enum(*list)
> mc = Module === self ? self : self.class
> list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, i}
> end
> def bit_enum(*list)
> mc = Module === self ? self : self.class
> list.flatten.each_with_index{|e, i| mc.const_set e.to_s.intern, 2 ** i}
> end
> end

I changed the method names to enum_constants and enum_bit_constants.

> probably could also combine this idea with the Enum module...

I wasn't sure what you meant by this. I tried changing the
above to be 'class Enum", and then have the method names
be "Enum.constants" and "Enum.bit_constants", but that didn't
seem to work. I checked Pickaxe 2, RAA, and RubyForge for
an Enum module, but didn't come up with anything. Then again,
this is the first time I tried searching RAA or RubyForge for some
module, so I'm probably doing it wrong...

0 new messages