17 Mar, 2010, Runter wrote in the 21st comment:
Votes: 0
Deimos said:
I'm working on converting my wilderness code, and rooms are indexed in a two-dimensional array by their X,Y coordinates. Not being able to pluck rooms out by their indexes would suck. :tongue:



So the problem is you have no way of knowing the location of a given node without knowing the position in the array? In other words, that data is not saved on the individual location data?

Of course array[x][y] works to pluck them out…
17 Mar, 2010, David Haley wrote in the 22nd comment:
Votes: 0
Well, if you already know where it is (where "it" could be a collection of nodes), why not index into the array directly? You wouldn't want to iterate over the whole thing, picking out the ones that have the right coordinates.

EDIT: re: your edit, I'm assuming that the idea is to have a collection of indices, and to iterate over that collection into the map. Otherwise yeah, just a[x][y].
17 Mar, 2010, Deimos wrote in the 23rd comment:
Votes: 0
Runter said:
So the problem is you have no way of knowing the location of a given node without knowing the position in the array? In other words, that data is not saved on the individual location data?

Of course array[x][y] works to pluck them out…

The data is saved on each node, but, as David pointed out, there's no reason to loop through hundreds of thousands of nodes, making hundreds of thousands of checks each time I want to pull a node by its coordinates.

The loop itself is for creating the ASCII map for the player. Given a player's current room coordinates, I needed to get a small subset of the surrounding rooms. To do this, I'm iterating the nested ranges (player_y - radius)..(player_y + radius) and (player_x - radius)..(player_x + radius).
17 Mar, 2010, Runter wrote in the 24th comment:
Votes: 0
Okay, but here is why I asked.

Originally in the thread you didn't like not being able to get the index of an iterator of a subset because it didn't correspond correctly to your original array.

If your data is saved on your array then:

foo[1..3].each do |i|
foo[i.x]
end


Would be relative to your overall array.
17 Mar, 2010, Deimos wrote in the 25th comment:
Votes: 0
Well, yes, but that doesn't work in my case, because "foo" is actually a two-dimensional array:

foo[1..3].each do |i| # iterates 3 arrays
# i is an array
i.each do |j| # iterates thousands of nodes for each array
#j is a node
node = foo[j.y][j.x]
end
end


Edit: Nevermind, I guess I could do i[1..3].each. Keep forgetting about this stuff…
17 Mar, 2010, Runter wrote in the 26th comment:
Votes: 0
#foo[1..3][1..3] 

foo[1..3].each do |i| # iterates 3 arrays
# i is an array
i[1..3].each do |j|
#node = j
# or
node = foo[j.y][j.x]
end
end


For completeness sake you can instantiate a range with Range.new(1, 3) instead of 1..3.
17 Mar, 2010, Runter wrote in the 27th comment:
Votes: 0
Using the ranges with each should be more efficient with larger samples of data to work with. I think using array[x][y] incrementing x and y and using array[x][y] again is going to result in a full lookup each time. The structure in C isn't a real array. You might would be better off if you wanted to take that approach if you used a two dimensional hash. Although I know us Ruby devs don't care so much about efficiency. We just want to develop fast, but if it were me I might would try to optimize this particular part of the code a little bit.


edit: Here's the code.

By Closure
VALUE
rb_ary_each(ary)
VALUE ary;
{
long i;

for (i=0; i<RARRAY(ary)->len; i++) {
rb_yield(RARRAY(ary)->ptr[i]);
}
return ary;
}



By Index
VALUE
rb_ary_aref(argc, argv, ary)
int argc;
VALUE *argv;
VALUE ary;
{
VALUE arg;
long beg, len;

if (argc == 2) {
if (SYMBOL_P(argv[0])) {
rb_raise(rb_eTypeError, "Symbol as array index");
}
beg = NUM2LONG(argv[0]);
len = NUM2LONG(argv[1]);
if (beg < 0) {
beg += RARRAY(ary)->len;
}
return rb_ary_subseq(ary, beg, len);
}
if (argc != 1) {
rb_scan_args(argc, argv, "11", 0, 0);
}
arg = argv[0];
/* special case - speeding up */
if (FIXNUM_P(arg)) {
return rb_ary_entry(ary, FIX2LONG(arg));
}
if (SYMBOL_P(arg)) {
rb_raise(rb_eTypeError, "Symbol as array index");
}
/* check if idx is Range */
switch (rb_range_beg_len(arg, &beg, &len, RARRAY(ary)->len, 0)) {
case Qfalse:
break;
case Qnil:
return Qnil;
default:
return rb_ary_subseq(ary, beg, len);
}
return rb_ary_entry(ary, NUM2LONG(arg));
}
17 Mar, 2010, Deimos wrote in the 28th comment:
Votes: 0
require "benchmark"

class Room
attr_reader :x, :y
def initialize( x, y )
@x, @y = x, y
end
end

class ArrayBenchmark

def initialize( width, height, radius )
@foo = Array.new( height, Array.new( width ) )
@offset_x = width / 2
@offset_y = height / 2
@radius = radius
@foo.each_index do |y|
@foo[y].each_index do |x|
@foo[y][x] = Room.new( x, y )
end
end
end

def run( iter )
Benchmark.bmbm do |bm|
bm.report( "each" ) do
@foo[(@offset_y-@radius)..(@offset_y+@radius)].each do |i|
i[(@offset_x-@radius)..(@offset_x+@radius)].each do |j|
@foo[j.y][j.x]
end
end
end
bm.report( "for..in" ) do
for y in ((@offset_y-@radius)..(@offset_y+@radius))
for x in ((@offset_x-@radius)..(@offset_x+@radius))
@foo[y][x]
end
end
end
end
end

end

bm = ArrayBenchmark.new( 1_000, 1_000, 10 )
bm.run( 10_000 )

Rehearsal ——————————————-
each 0.000000 0.000000 0.000000 ( 0.000720)
for..in 0.000000 0.000000 0.000000 ( 0.000617)
———————————- total: 0.000000sec

user system total real
each 0.000000 0.000000 0.000000 ( 0.000698)
for..in 0.000000 0.000000 0.000000 ( 0.000601)


I don't really see much of a quantifiable difference between the two performance-wise.




Edit: Actually, I just noticed I was doing "@foo[j.x][j.y]", rather than simply "j" in the each() test. After having changed it, they're still close enough to call it a tie.
17 Mar, 2010, JohnnyStarr wrote in the 29th comment:
Votes: 0
Runter, you seem to know quite a bit about Ruby's C API. I've been meaning to learn a bit more
about it. There are several things I would like to create for my own project, and think it would be
best to do this in C. Can you suggest any books or resources to get a better understanding of the API?

I of course have Ruby's source, but it is a bit hard to understand without a reference guide.

EDIT:
I have been meaning to ask more about the Symbol class. When you create a symbol, is it stored as a
text value or numeric value? For example, in C you can use #define to create a symbolic constant: CON_PLAYING = 1
So if you compare CON_PLAYING == 1 then you are comparing numeric values. In Ruby if you compare say: @state == :con_playing, does Ruby define the value of the :symbol as the text "con_playing" or is it
defined as some sort of numeric ID? Or is it the char number value of the symbol?
17 Mar, 2010, Runter wrote in the 30th comment:
Votes: 0
Yeah, my testing seems to confirm that it comes out a wash. Probably from other things the closures have going on.
17 Mar, 2010, David Haley wrote in the 31st comment:
Votes: 0
JohnnyStarr said:
In Ruby if you compare say: @state == :con_playing, does Ruby define the value of the :symbol as the text "con_playing" or is it
defined as some sort of numeric ID?

It's not the literal text con_playing. The implementation details are supposedly unimportant, but basically the symbols are (IIRC) guaranteed to be unique things only equal to other symbols of the same name. I would imagine that they're stored as objects of type 'symbol', and they have some kind of guaranteed unique ID (but I haven't checked).

You can think of them as type-safe enums or #defines, sort of.
17 Mar, 2010, Runter wrote in the 32nd comment:
Votes: 0
JohnnyStarr said:
Runter, you seem to know quite a bit about Ruby's C API. I've been meaning to learn a bit more
about it. There are several things I would like to create for my own project, and think it would be
best to do this in C. Can you suggest any books or resources to get a better understanding of the API?

I of course have Ruby's source, but it is a bit hard to understand without a reference guide.


Well, the book most people in the Ruby community go to when learning this seems to be "the pickaxe book". That's where I went too. There's an online version (albeit a little dated.) that you can access here. If you go to the chapter on extending Ruby it will get you started. It's going to expect you to know C, of course, but it gives some insight.
http://www.ruby-doc.org/docs/Programming...

If you want to buy a book it's a good one to have on your shelf in current edition.

http://pragprog.com/titles/ruby/programm...

There's also the doxygen page for reference.
http://www.ruby-doc.org/doxygen/current/...
17 Mar, 2010, JohnnyStarr wrote in the 33rd comment:
Votes: 0
:biggrin: ty
17 Mar, 2010, Runter wrote in the 34th comment:
Votes: 0
Quote
I have been meaning to ask more about the Symbol class. When you create a symbol, is it stored as a
text value or numeric value? For example, in C you can use #define to create a symbolic constant: CON_PLAYING = 1
So if you compare CON_PLAYING == 1 then you are comparing numeric values. In Ruby if you compare say: @state == :con_playing, does Ruby define the value of the :symbol as the text "con_playing" or is it
defined as some sort of numeric ID? Or is it the char number value of the symbol?


Well, it's probably text but they're a shared literal value. :something refers to the same object ID in every context. So your con_playing stores the string and assigns it an ID. It just so happens that symbols use the Object standard equality check that any Object can use if it isn't overridden. It simply checks to see if two objects are exactly the same object. Symbols are just an interesting convention but they're really the same as other objects.

But we wouldn't really want them to be different anyways.
17 Mar, 2010, David Haley wrote in the 35th comment:
Votes: 0
It might even be internalized string char* pointers under the hood, so equality testing would be direct pointer equality testing.
17 Mar, 2010, Runter wrote in the 36th comment:
Votes: 0
David Haley said:
It might even be internalized string char* pointers under the hood, so equality testing would be direct pointer equality testing.


Yes,

static VALUE
rb_obj_equal(obj1, obj2)
VALUE obj1, obj2;
{
if (obj1 == obj2) return Qtrue;
return Qfalse;
}
20.0/36