Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > [Solution]: Lisp partial solution - meta-programming help

Reply
Thread Tools

[Solution]: Lisp partial solution - meta-programming help

 
 
Louis J Scoras
Guest
Posts: n/a
 
      10-03-2005
------=_Part_17982_9256152.1128372048201
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

Hi all;

I started to give the lisp quiz a go. From the begining, I resolved to try
for a solution such that the map can be set up in a declarative manner.

I wanted it to look something like the attr_ class methods and ActiveRecord=
s
`associations`; however, setting up something like this seems to be more
work than I thought it would be. Following is some working code, but it's
pretty ugly. Setting the class variable in this manner just feels very
hackish, yet it's the only way I could seem to get this to work.

The problem was this: how to write the methods so that they don't have to b=
e
explicitly overwritten in the subclasses, and yet they can reference these
concrete classes state?

Anyone with a bit more ruby experiance have any refactoring ideas?


--
Lou


module LocationClassMethods
def exit_to(place,direction,opts=3Dnil)
portal =3D (opts[:through] || opts[:via] if opts.respond_to?:[]) || 'door'
class_variable_set(:@@exits, []) unless class_variables.include?(:@@
exits.to_s)
exits =3D class_variable_get(:@@exits)
exits << Exit.new(direction,portal)
end

def description(desc)
class_variable_set(:@@description, desc)
end

def exits
class_variable_get(:@@exits)
end

def get_description
class_variable_get(:@@description)
end

end

class Location
def self.inherited(sub)
sub.extend(LocationClassMethods)
end

def initialize
@exits =3D self.class.exits
@description =3D self.class.get_description
end

def describe
puts @description
describe_exits
end

def describe_exits
@exits.each { |e| puts e.describe }
end
end

class Attic < Location
exit_to :living_room, :down, :via =3D> 'staircase'

description 'You are in the attic of the wizard\'s house. There is a giant
welding torch in the corner.'
end

class Garden < Location
exit_to :living_room, :west

description 'You are standing in a beautiful garden. There is a well in
front of you.'
end

class LivingRoom < Location
exit_to :attic, :up, :via =3D> 'staircase'
exit_to :garden, :east

description 'You are in the living-room of a wizard\'s house. There is a
wizard snoring loudly on the couch.'
end


a =3D Attic.new
a.describe

------=_Part_17982_9256152.1128372048201--


 
Reply With Quote
 
 
 
 
Edward Faulkner
Guest
Posts: n/a
 
      10-03-2005
--liOOAslEiF7prFVr
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Tue, Oct 04, 2005 at 05:40:51AM +0900, Louis J Scoras wrote:
> Anyone with a bit more ruby experiance have any refactoring ideas?


Here's a sketch. It doesn't have the full feature set (portals, etc),
but that should be easy to add. Instead of messing around with class
variables, we simply define methods that return the values we want.
The data is stored implicitly within blocks.

class Location
def self.description(d)
# Even though we're using a constant method name, we use
# "define_method" instead of "def" in order to form a closure over
# the local variable "d".
define_method(:describe) do
d
end
end

def self.exit_to(direction,location)
# @exits is a member of the class object itself, not the instances
@exits ||= {}
@exits[direction] = location
e = @exits
define_method(:exits) do
e
end
end
end

class A < Location
exit_to :north, :b
exit_to :east, :garden
description "You're at point A. It's very boring."
end

class B < Location
exit_to :south, :a
description "Point B is even more boring than Point A."
end

regards,
Ed

--liOOAslEiF7prFVr
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: Digital signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)

iD8DBQFDQbSRnhUz11p9MSARAu6uAJ93tR4X5KKjXKA9TXIN0o pHwozouQCgmYsu
PL0EQ3KtRkoxwOfTVkZZ6Fk=
=TOsh
-----END PGP SIGNATURE-----

--liOOAslEiF7prFVr--


 
Reply With Quote
 
 
 
 
Sean O'Halpin
Guest
Posts: n/a
 
      10-03-2005
Along the same lines, as I am unlikely to pursue this any further,
here is my half-baked doodle towards a game DSL

You can move about (using 'east', 'west', 'upstairs', etc.) but nothing els=
e.

Regards,
Sean

-- CODE --
module Attributes
def has(*names)
self.class_eval {
names.each do |name|
define_method(name) {|*args|
if args.size > 0
instance_variable_set("@#{name}", *args)
else
instance_variable_get("@#{name}")
end
}
end
}
end

end

module Directions
def directions(*directions)
directions.each do |name|
self.class.class_eval {
define_method(name) {
dest =3D @location.exits[name]
if dest
@location =3D @rooms[dest[1]]
look
else
puts "You can't move in that direction"
end
}
}
end
end
end

class GameObject
extend Attributes
has :identifier, :name, :description
def initialize(identifier, &block)
@identifier =3D identifier
instance_eval &block
end
end

class Thing < GameObject
has :location
end

class Room < GameObject
has :exits

def initialize(identifier, &block)
# put defaults before super - they will be overridden in block (if at a=
ll)
super
end

end

class Game
include Directions

attr_accessor :name, :rooms, :location, :things

def initialize(name, &block)
@name =3D name
@rooms =3D {}
@things =3D {}

# read game definition
instance_eval &block

end

def room(identifier, &block)
@rooms[symbol(identifier)] =3D Room.new(symbol(identifier), &block)
end

def thing(identifier, &block)
@things[symbol(identifier)] =3D Thing.new(symbol(identifier), &block)
end

def symbol(s)
s.to_s.to_sym
end

def start(room_identifier)
@location =3D @rooms[room_identifier]
end

def describe_path(direction, path)
"There is a #{path} going #{direction}."
end

def describe_exits(location)
location.exits.map {|direction, (path, destination)|
describe_path(direction, path)
}
end

def describe_floor(location)
@things.select{|key, thing| thing.location =3D=3D
location.identifier}.map{|key, thing| "There is a #{thing.description}
here."}
end

def main_loop
while input =3D gets
input.chomp!
case input
when 'exit', 'quit'
break
when 'help'
puts "Sorry pal! You're on your own here "
else
begin
instance_eval input
rescue Exception =3D> e
puts e.to_s
puts "Eh?"
end
end
end
end


# commands

def look
puts location.description
puts describe_exits(location)
puts describe_floor(location)
end

def quit
break
end

end

def game(name, &block)
g =3D Game.new(name, &block)
g.look
g.main_loop
end

# Game definition

game "Ruby Adventure" do

directions :east, :west, :north, :south, :up, :down, :upstairs, :downstai=
rs

room :living_room do
name 'Living Room'
description "You are in the living-room of a wizard's house. There
is a wizard snoring loudly on the couch."
exits :west =3D> [:door, :garden],
:upstairs =3D> [:stairway, :attic]
end

room :garden do
name 'Garden'
description "You are in a beautiful garden. There is a well in
front of you."
exits :east =3D> [:door, :living_room]
end

room :attic do
name "Attic"
description "You are in the attic of the wizard's house. There is
a giant welding torch in the corner."
exits :downstairs =3D> [:stairway, :living_room]
end

thing :whiskey_bottle do
name 'whiskey bottle'
description 'half-empty whiskey bottle'
location :living_room
end

thing :bucket do
name 'bucket'
description 'rusty bucket'
location :living_room
end

thing :chain do
name 'chain'
description 'sturdy iron chain'
location :garden
end

thing :frog do
name 'frog'
description 'green frog'
location :garden
end

start :living_room

end
-- END --


 
Reply With Quote
 
Louis J Scoras
Guest
Posts: n/a
 
      10-04-2005
------=_Part_439_20500209.1128386563313
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

Okay cool. So closures are the way to go. That's the way I would have done
it in perl, but I didn't know if all the OO magic was prefered way in Ruby.
Plus, I was looking at the Pickaxe reference for the Module Class
documentation =3D)

That helps a lot, thanks.


--
Lou

------=_Part_439_20500209.1128386563313--


 
Reply With Quote
 
James Edward Gray II
Guest
Posts: n/a
 
      10-04-2005
On Oct 3, 2005, at 6:01 PM, Mark J.Reed wrote:

> I'm trying to come up with a way to have my cake and eat it, too.
> Is there
> any way to hook into irb and have some code executed every time the
> prompt
> is displayed, like $PROMPT_COMMAND in bash? That would let me work
> around
> this . . .


This was quite problematic when I was building my own version. I
tried to get around it with my $stringify global, but that requires
you to spell out all allowed words.

I'm very it interested in the answers to your irb questions, though,
so we will both await the responses from the gurus...

James Edward Gray II

P.S. Please do share your code at some point... Looks great!



 
Reply With Quote
 
Sean O'Halpin
Guest
Posts: n/a
 
      10-04-2005
Here's one way to hook into irb's eval loop:

module IRB
class Context
def evaluate(line, line_no)
value =3D @workspace.evaluate(self, line, irb_path, line_no)
puts "value =3D #{value}" # do something with returned value here
value
end
end
end

Regards,

Sean


 
Reply With Quote
 
Sean O'Halpin
Guest
Posts: n/a
 
      10-04-2005
Oops - that should have been:

module IRB
class Context
def evaluate(line, line_no)
value =3D @workspace.evaluate(self, line, irb_path, line_no)
puts "value =3D #{value}"
set_last_value(value)
end
end
end

(Note set_last_value)

Sean


 
Reply With Quote
 
James Edward Gray II
Guest
Posts: n/a
 
      10-04-2005
On Oct 3, 2005, at 8:11 PM, Mark J.Reed wrote:

> $objects.find_all do
> |obj|


I had to double-check that to convince myself it works. I thought I
remembered reading more than once that the block arguments had to be
on the same line as the start of the block.

Has that been changed, or is my memory fuzzy?

James Edward Gray II



 
Reply With Quote
 
Adam Shelly
Guest
Posts: n/a
 
      10-04-2005
On 10/3/05, James Edward Gray II <> wrote:
> This was quite problematic when I was building my own version. I
> tried to get around it with my $stringify global, but that requires
> you to spell out all allowed words.


I solved it by letting method_missing fill in the allowed words itself.
The trick is that all the allowed words are used in Game#initialize,
so after the global Game object is constructed, any additional unknown
words must be invalid commands.

class Game
def initialize
@objects =3D [whiskey_bottle, frog, bucket, chain]
#...
end
#...
end

$g =3D Game.new
$valid_words =3D []

def method_missing symbol, *args
#if the game has not started, add the symbol to the list of valid to=
kens
if !$g
$valid_words << symbol
symbol.to_s
#if the game has started, see if it is a token
elsif $words.include? symbol
symbol.to_s
#otherwise, it is an unknown command.
else
puts "> #{symbol} #{args.join ' '}" if !$Interactive
puts "Sorry, I don't understand #{symbol}"
end
end



-Adam


 
Reply With Quote
 
Adam Shelly
Guest
Posts: n/a
 
      10-04-2005
oops:
> elsif $words.include? symbol

should be:
elsif $valid_words.include? symbol


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Nice historical Musical - VERY RELAXING - about LISP history -fundamental ideas of LISP nanothermite911fbibustards C++ 0 06-16-2010 09:47 PM
Nice historical Musical - VERY RELAXING - about LISP history -fundamental ideas of LISP nanothermite911fbibustards Python 0 06-16-2010 09:47 PM
pat-match.lisp or extend-match.lisp in Python? ekzept Python 0 08-10-2007 06:08 PM
Re: Partial 1.0 - Partial classes for Python Thomas Heller Python 13 02-08-2007 08:12 PM
Why? Partial Class within a Partial class Billy ASP .Net 2 02-01-2006 09:10 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57