Forum: Ruby Data Structure for n-dimension values with defaults

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Ec9233451f7c6ba37a83388b87a1f565?d=identicon&s=25 Phrogz (Guest)
on 2006-03-27 01:28
(Received via mailing list)
Help! I can't figure out how to store a rather particular set of data.

An object in my system has properties. Each property has a value.
However, the value of each property can change based on a number of
"build axes". There are an arbitrary number of orthogonal build axes,
none of which is more important than another. (However, if it's needed,
I am happy to impose a particular ordering on them.)

Each build axis has a number of 'notches', or discrete values along the
axis. In addition to all the notches, there is one special notch named
"Master". A value set in this notch is a fallback, default value if
other notches in the axis have no values.

So, for example, assume I'm talking about a Text object. One of it's
properties is "textString".
The system has two build axes: "Language" and "Rating".

The "Language" axis has "French", "English", "German", and "Jibbrish"
notches in addition to the Master notch. The "Rating" axis has "G",
"PG", "R", and "X" values.

Here's a monospace-font diagram of the values I want to store:

                -------------------Language-------------------------

                Master    English      French     German    Jibbrish
 R    Master    (yell)       -            -        Ach!        -
 A         G       -        Wow!         Oh!        -          -
 T        PG       -     Holy Crap!    Merde!       -          -
 I         R       -     Holy Shit!   Mon Dieu!     -          -
 N         X     $!@*        -            -        XXX         -
 G

With the above, the textString property should have a value of "Ach!"
if the Language axis is set to German and the Rating axis is set to G,
PG, or R.

If the Rating is set to "X", then the textString should be set to
"$!@*" regardless of the Language axis setting...except for if the
Language is set to German, in which case it should be "XXX"

If the Language is set to "Jibbrish", then the value is "(yell)",
except when the Rating is "X".

If the Rating is set to "PG" and the Language is set to "English", then
the textString value should be set to "Holy Crap!". And so on for other
values that are explicitly set for all axis combinations.


Hopefully that example is clear enough to show that explicit values win
over master values.

So, how the heck would you store this sort of information? I mostly
don't even care about speed.
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-03-27 01:45
(Received via mailing list)
On Mar 26, 2006, at 6:28 PM, Phrogz wrote:

> the
> "PG", "R", and "X" values.
>  N         X     $!@*        -            -        XXX         -
> If the Language is set to "Jibbrish", then the value is "(yell)",
> win
> over master values.
>
> So, how the heck would you store this sort of information? I mostly
> don't even care about speed.
>
>

An array of hashes? Actually a hash of structs would probably be better

Row = Struct.new(:master, :english, :french, :german, :gibberish)

table = {
                       :G => Row.new('(yell)', nil, nil, 'Ach!', nil),
                       :PG => Row.new(nil, 'Wow!', 'Oh!', nil, nil),
                       :R =>  Row.new(nil, 'Holy Shit!', 'Mon Dieu!',
nil, nil),
                       :X =>  Row.new('$!@*', nil, nil, 'XXX', nil)
}

def table.get(rating, language)
        res = self[rating].send(language)
         if res.nil?
             self[rating].master
        else
             res
        end
end



irb(main):040:0> table.get(:X, :german)
=> "XXX"
irb(main):041:0> table.get(:X, :english)
=> "$!@*"
Ec9233451f7c6ba37a83388b87a1f565?d=identicon&s=25 Phrogz (Guest)
on 2006-03-27 01:54
(Received via mailing list)
Close, but it fails from the ordered-axis problem I've been seeing.

puts table.get( :R, :german )
#=> nil

that should be "Ach!"
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-03-27 02:14
(Received via mailing list)
On Mar 26, 2006, at 6:53 PM, Phrogz wrote:

> Close, but it fails from the ordered-axis problem I've been seeing.
>
> puts table.get( :R, :german )
> #=> nil
>
> that should be "Ach!"
>
>

Improved version:
% cat table_structure.rb
Row = Struct.new(:master, :english, :french, :german, :gibberish)

table = {
   :master => Row.new('(yell)', nil, nil, 'Ach!', nil),
   :G => Row.new(nil,  'Wow!', 'Oh!', nil, nil),
   :PG => Row.new(nil, 'Holy Crap!', 'Merde!', nil, nil),
   :R => Row.new(nil, 'Holy Shit!', 'Mon Dieu!', nil, nil),
   :X => Row.new('$!@*', nil, nil, 'XXX', nil)
}

def table.get(rating, language)
   res = self[rating].send(language)
   if res.nil?
     res = self[rating].master
   end
   if res.nil?
     res = self[:master].send(language)
   end
   if res.nil?
     res =  self[:master].master
   end
   res
end

puts table.get(:R, :german)
puts table.get(:X, :german)
puts table.get(:X, :english)


% ruby table_structure.rb
Ach!
XXX
$!@*
Ec9233451f7c6ba37a83388b87a1f565?d=identicon&s=25 Phrogz (Guest)
on 2006-03-27 02:24
(Received via mailing list)
Cool, that seems to do it. So now, I just need to figure out how that
resolution algorithm scales in the n-dimensional case when I'm trying
to optimize for the fewest number of master values involved. And, as
Ryan Davis pointed out on IRC, what my logic is when conflicting values
have the same weight (and if I should even be able to get into such a
situation).

This is helpful, though. I was previously trying to approach the
problem from some sort of default-hash value solution. This has made
apparent that I need to be storing n-dimensional data, and only if I
find a nil value at the intersection of all axes do I go into a
separate resolution mode for searching for the best value.
C0ec5a090e5df61f717849e6846d5246?d=identicon&s=25 Ernest Obusek (Guest)
on 2006-03-27 19:50
(Received via mailing list)
I'm new to Ruby so perhaps this is obvious to you but not to me.  Can
anyone tell me what is wrong with my simple script below:

$ cat t.rb

#!/usr/bin/ruby

puts "Timer set to #{ARGV[0]} seconds.  Press <ENTER> to start the
countdown."
gets


$ ./t.rb 60
Timer set to 60 seconds.  Press <ENTER> to start the countdown.
./t.rb:4:in `gets': No such file or directory - 60 (Errno::ENOENT)
         from ./t.rb:4

It seems to think the argument of 60 should be a file or
directory...  ????

Thanks,

Ernest
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-08-03 14:21
(Received via mailing list)
On Tue, 28 Mar 2006, Ernest Obusek wrote:

>
> Ernest
you have not given a receiver to gets and the default one is ARGF (see
pickaxe
for desc).  try

   STDIN.gets

hth.

-a
36194a022cb2afb981bfdf720fc67d82?d=identicon&s=25 Eric Armstrong (Guest)
on 2006-08-03 14:22
(Received via mailing list)
ara.t.howard@noaa.gov wrote:
>
> you have not given a receiver to gets and the default one is ARGF (see
> pickaxe for desc).  try
>
>   STDIN.gets
>
That almost makes sense, except for the name of the receiver.
gets is supposed to read from a file, if specified. Otherwise
it reads from piped input (according to the ancient books I've
consulted).

I can understand that you would need to specify a different
receiver to specify "current console device". But surely
the input pipe is standard input, yes? So STDIN is the current
console device, and that's not the same as standard in???

Signed, Confused in Peoria.
:_)
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-08-03 14:22
(Received via mailing list)
On Tue, 28 Mar 2006, Eric Armstrong wrote:

>>> ????
>
> I can understand that you would need to specify a different
> receiver to specify "current console device". But surely
> the input pipe is standard input, yes? So STDIN is the current
> console device, and that's not the same as standard in???
>
> Signed, Confused in Peoria.
> :_)
>

yes and no.  first off gets is a method of IO and it does read from
piped
input.  however, there is a magic variable called ARGF which is the list
of
all files on the command line, or stdin if none are given.  so 'cat', in
ruby
could be written as

   ARGF.each{|line| print line}

and this would work with

   cat one | ruby a.rb
   ruby a.rb < two
   ruby a.rb one two three

make sense?  see ARGF in pickaxe for more.

regards.

-a
36194a022cb2afb981bfdf720fc67d82?d=identicon&s=25 Eric Armstrong (Guest)
on 2006-08-03 14:22
(Received via mailing list)
ara.t.howard@noaa.gov wrote:
 > gets is a method of IO and it does read from piped input.
 > however, there is a magic variable called ARGF which is the list of
 > all files on the command line, or stdin if none are given.  so 'cat',
 > in ruby could be written as
 >
 >   ARGF.each{|line| print line}
 >
So far, so good. Makes perfect sense.
What is more perplexing is that the formula for console I/O
would look like this:

    puts prompt_string
    STDIN.gets               --succeeds?

Why would standard input work there, but fail in this case:

    puts prompt_string
    gets                     --fails?

If no file is specified on the command line, why doesn't that
invocation gets do the same thing as STDIN.gets?
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-08-03 14:22
(Received via mailing list)
On Tue, 28 Mar 2006, Eric Armstrong wrote:

> would look like this:
> invocation gets do the same thing as STDIN.gets?
ruby doesn't know that you didn't mean to say to read the file '60',
which is
on the command line.

when you use ARGF, deliberately or not, you telling ruby that ANY
command line
argument can be opened are read from.  in your case you gave '60' and
this
file does not exist.  your code will work if you parse the command line
first:

     harp:~ > cat a.rb
     print ARGF.read

     harp:~ > echo 42|ruby a.rb 60
     a.rb:1:in `read': No such file or directory - 60 (Errno::ENOENT)
             from a.rb:1


     harp:~ > cat a.rb
     ARGV.shift
     print ARGF.read

     harp:~ > echo 42|ruby a.rb 60
     42


regards.

-a
36194a022cb2afb981bfdf720fc67d82?d=identicon&s=25 Eric Armstrong (Guest)
on 2006-08-03 14:22
(Received via mailing list)
Brilliant! Thanks much for the explain-o.

It wasn't my code, but I learned a lot anyway.
This topic is locked and can not be replied to.