Hashes and Blocks (Syntactical/Feature question)

P

Pete Elmore

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I haven't been able to find any documentation on the syntax for a
certain feature, and am not sure the feature exists. What I would like
to do is add a block of code to a hash, so I could do something like this:

state = 0
states = {
0 => { puts "State 0"; state = 1 },
1 => { puts "State 1"; state = 0 }
}

However, I haven't found any documentation on doing anything like this,
so I'm not sure you can. What I'm doing right now is a little ugly:

state = 0
states = {
0 => "puts 'State 0'; state = 1",
1 => "puts 'State 1'; state = 0"
}

while a_condition
eval states[state]
end

Obviously, keeping code as a string and then using eval gets pretty
hairy if it gets any more complicated than this. The ability to pass
blocks as data is one of Ruby's best features, and this would allow
programmers to avoid constructs like
state = X
data.each { |datum|
if state == 0
code
elsif state == 1
other code
...
}

(If this is repugnant to the nature of Ruby, there's a better way to do
it, or it already exists and I've missed it, please forgive my n00b
question!)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Debian - http://enigmail.mozdev.org

iD8DBQFBgu+Yv24lB609Ih8RAvYhAKCSYQyZcpT/nj6LJJCBlxKHhkceUACfVca5
hrLw6eGQwZ7XZh2B2an/toc=
=uKEd
-----END PGP SIGNATURE-----
 
A

Andreas Schwarz

Pete said:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I haven't been able to find any documentation on the syntax for a
certain feature, and am not sure the feature exists. What I would like
to do is add a block of code to a hash, so I could do something like this:

state = 0
states = {
0 => { puts "State 0"; state = 1 },
1 => { puts "State 1"; state = 0 }
}

state = 0
states = {
0 => Proc.new { puts "State 0"; state = 1 },
1 => Proc.new { puts "State 1"; state = 0 }
}

while a_condition
states[state].call
end
 
G

Gavin Sinclair

I haven't been able to find any documentation on the syntax for a
certain feature, and am not sure the feature exists. What I would like
to do is add a block of code to a hash, so I could do something like this:
state = 0
states = {
0 => { puts "State 0"; state = 1 },
1 => { puts "State 1"; state = 0 }
}

It's pretty easy:

state = 0
states = {
0 => lambda { puts "State 0"; state = 1 },
...
}

while a_condition
states[state].call
end

Cheers,
Gavin
 
R

Robert Klemme

Gavin Sinclair said:
I haven't been able to find any documentation on the syntax for a
certain feature, and am not sure the feature exists. What I would like
to do is add a block of code to a hash, so I could do something like
this:
state = 0
states = {
0 => { puts "State 0"; state = 1 },
1 => { puts "State 1"; state = 0 }
}

It's pretty easy:

state = 0
states = {
0 => lambda { puts "State 0"; state = 1 },
...
}

while a_condition
states[state].call
end

And you can even define default behavior:

state = 0
states = Hash.new( lambda { puts "fallback" } ).update(
0 => lambda { puts "State 0"; state = 1 },
1 => lambda { puts "State 1"; state = 0 }
)

loop do
states[rand 100].call
end

or even (for individual fallbacks)

states = Hash.new { |h,k| h[k] = lambda { puts "fallback for #{k}" } }
..update(
0 => lambda { puts "State 0"; state = 1 },
1 => lambda { puts "State 1"; state = 0 }
)
10.times {|i| states.call }

State 0
State 1
fallback for 2
fallback for 3
fallback for 4
fallback for 5
fallback for 6
fallback for 7
fallback for 8
fallback for 9
=> 10

Kind regards

robert
 
J

Jim Haungs

What's a hashtable with names pointing to code blocks?
Hmm... Sounds like a class!

What you're trying to do is an example of the State pattern, which is
often implemented with sensibly-named states (as opposed to the
numbers in the example here). The state names are really method names;
you perform the operation for some state X by sending the X message to
an instance of the State class. The class can also encapsulate the
current state of the machine, track state transitions, or whatever.

And if you need multiple instances of the state machine, you just
create a new instance of the State class.

With the hash table scheme, the cohesion is poor because the current
machine state is separated from the operations on that state.

Gavin Sinclair said:
I haven't been able to find any documentation on the syntax for a
certain feature, and am not sure the feature exists. What I would like
to do is add a block of code to a hash, so I could do something like
this:
state = 0
states = {
0 => { puts "State 0"; state = 1 },
1 => { puts "State 1"; state = 0 }
}

It's pretty easy:

state = 0
states = {
0 => lambda { puts "State 0"; state = 1 },
...
}

while a_condition
states[state].call
end

And you can even define default behavior:

state = 0
states = Hash.new( lambda { puts "fallback" } ).update(
0 => lambda { puts "State 0"; state = 1 },
1 => lambda { puts "State 1"; state = 0 }
)

loop do
states[rand 100].call
end

or even (for individual fallbacks)

states = Hash.new { |h,k| h[k] = lambda { puts "fallback for #{k}" } }
.update(
0 => lambda { puts "State 0"; state = 1 },
1 => lambda { puts "State 1"; state = 0 }
)
10.times {|i| states.call }

State 0
State 1
fallback for 2
fallback for 3
fallback for 4
fallback for 5
fallback for 6
fallback for 7
fallback for 8
fallback for 9
=> 10

Kind regards

robert
 
R

Robert Klemme

Jim Haungs said:
What's a hashtable with names pointing to code blocks?
Hmm... Sounds like a class!

Yeah, you can view it that way.
What you're trying to do is an example of the State pattern, which is
often implemented with sensibly-named states (as opposed to the
numbers in the example here). The state names are really method names;

I beg to differ at this point: state names are usually translated to class
names. Events are translated to methods (see the example below).
you perform the operation for some state X by sending the X message to
an instance of the State class.

Again, I'd rephrase that to "you perform some operation Y for state X by
sending the message Y to an instance of X.
The class can also encapsulate the
current state of the machine, track state transitions, or whatever.

State transitions are tracked by exchanging the instance.
And if you need multiple instances of the state machine, you just
create a new instance of the State class.
Yep.

With the hash table scheme, the cohesion is poor because the current
machine state is separated from the operations on that state.

That's true although it's what the OP asked for. :)

Kind regards

robert


Example

class Window
def initialize
@state = WinClose.new
end

# actions
def look() @state.look end

# queries
def open?() @state.open? end

# transitions
def open() @state = @state.open end
def close() @state = @state.close end

private
class WinOpen
# actions
def look() "beautiful landscape" end

# queries
def open?() true end

# transitions
def open() raise "You can't open an open window" end
def close() WinClose.new end
end

class WinClose
# actions
def look() nil end

# queries
def open?() false end

# transitions
def open() WinOpen.new end
def close() raise "You can't close a closed window" end
end
end

Gavin Sinclair said:
On Saturday, October 30, 2004, 11:29:20 AM, Pete wrote:

I haven't been able to find any documentation on the syntax for a
certain feature, and am not sure the feature exists. What I would like
to do is add a block of code to a hash, so I could do something like
this:

state = 0
states = {
0 => { puts "State 0"; state = 1 },
1 => { puts "State 1"; state = 0 }
}

It's pretty easy:

state = 0
states = {
0 => lambda { puts "State 0"; state = 1 },
...
}

while a_condition
states[state].call
end

And you can even define default behavior:

state = 0
states = Hash.new( lambda { puts "fallback" } ).update(
0 => lambda { puts "State 0"; state = 1 },
1 => lambda { puts "State 1"; state = 0 }
)

loop do
states[rand 100].call
end

or even (for individual fallbacks)

states = Hash.new { |h,k| h[k] = lambda { puts "fallback for #{k}" } }
.update(
0 => lambda { puts "State 0"; state = 1 },
1 => lambda { puts "State 1"; state = 0 }
)
10.times {|i| states.call }

State 0
State 1
fallback for 2
fallback for 3
fallback for 4
fallback for 5
fallback for 6
fallback for 7
fallback for 8
fallback for 9
=> 10

Kind regards

robert

 
P

Pete Elmore

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Thanks for all of the helpful responses!

| states = Hash.new( lambda { puts "fallback" } ).update(
That's interesting; it had not occurred to me to use the default hash
value like that.

| What you're trying to do is an example of the State pattern, which is
| often implemented with sensibly-named states (as opposed to the
| numbers in the example here).
Yeah, actually, I was simplifying it a little; I used names for the states.
| With the hash table scheme, the cohesion is poor because the current
| machine state is separated from the operations on that state.
Very good point. But writing a class to handle a state machine for a
program that was about thirty lines could have made it quite a bit
longer/more complicated. ;)

The program actually generated Ruby code that was used by another Ruby
program to perform the tedious task of writing assembly language for the
PIC microcontroller (whew).
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Debian - http://enigmail.mozdev.org

iD8DBQFBigJTv24lB609Ih8RAtbdAJsHdHSY7pik1xWT8bYSGkef7sIpqgCfV2JQ
QN9AALBzXPT1Puc6DbIx+Sw=
=KcEB
-----END PGP SIGNATURE-----
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top