Main Menu
  • Welcome to The RPG Maker Resource Kit.

[RESOLVED] Equivalent Constructions?

Started by modern algebra, January 30, 2008, 07:16:09 AM

0 Members and 1 Guest are viewing this topic.

modern algebra


for i in 0...array.size
  p array[i]
end


Hey, I know that this is an inefficient way to act upon every element in an array, and I know that this is better:


array.each {|i| print i}


What I was wondering was two things:

(1)

Is this:


for i in array
  print i
end


equivalent to the .each way? Or is it done in a different way? And

(2) Is there a way in the .each method to retrieve the current element's index?

i.e. If you don't want to print out i but instead print out index, i. For purposes of demonstrating what I mean, this would be done like the following by the first method of acting on every element of the array


for i in 0...array.size
  p i, array[i]
end

Zeriab

(1)
My guess is that it is faster than the for i in 0...array.size while slower than the .each method.
My reasoning:
for i in 0...array.size
  p array[i]
end

# Is pretty much equivalent to
(0...array.size).each {|i| p array[i]}

There are probably some variable scope issues if you tried that code, but you should get the idea.
You iterator over another object to get an object which is then used to retrieve an object from the array.

Accordingly to Blizzard the .each is faster than using a for loop. How fast and if the different is significant I do not know. Though considering the different scope I can easily imagine that using .each is faster.

(2)
There is no direct way to retrieve the index. You can use array.index(i) to retrieve the index, but not only is that slow, it only returns the index of the first matching value. Searching for elements in an array is so slow that in almost all cases using the for i in 0...array.size way is faster. In addition it is easier to manage and keep track of.
Only use the iterator way (for element in array) if the index is irrelevant.

If the index is relevant then you could this:
array.each_with_index {|element, index| p index, element}


If you want to know the .each method is implemented as the Visitor Pattern
It can be used on any Enumerable collection.
There exist collections where the order the elements are retrieved can be stochastic and some where the concept of indexes doesn't make sense.
The index with the .each_with_index method shows how many elements was retrieved before it.

*hugs*
- Zeriab

modern algebra

Cool. Thanks; that answered my question perfectly. You're awesome, Zeriab  :D

I didn't know about .each_with_index either. Thanks again.

Zeriab

How you conduct a test on the speed of the different constructions?
I want to know how fast each construction runs. I want you test this.
I am sure it will be a useful experience ^^

So, how do you plan to do it?
In addition to put forward the average speed difference you should explain how the tests was conduct in a convincing way.
Sort of the proof that you have done your work properly.

Good luck
*hugs*
- Zeriab

modern algebra

*cries

I suppose I would try this:

Take an arbitrarily large array, and apply each construction successively (each doing the same thing; not print statements for sure :P). Upon completion of each construction's algorithm, record the time taken for the algorithm to complete.

Maybe I should try this for various types of algorithms - some simple, some heavy, some requiring index. I should also probably keep track of CPU Usage.


Zeriab

Try it. Tell me how it went.
If you learned anything, tell me that as well

modern algebra

Failure :P

I tried this code:


class Test_Object
  attr_reader :id
  attr_reader :test_variable
  def initialize (id)
    @id = id
    @test_variable = 0
  end
  def test
    # This is just a random method call
    @test_variable += 1
  end
end


class Test
  def initialize
    test_array = []
    for i in 0...1200000
      test_array.push (Test_Object.new (i))
    end
    p 'Test A Begins'
    for i in 0...test_array.size
      test_array[i].test
    end
    p 'Test A Completed'
    p 'Test B begins'
    for i in test_array
      i.test
    end
    p 'Test B completed'
    p 'Test C begins'
    test_array.each {|i| i.test}
    p 'Test C completed'
  end
end


Under the basic idea that the best test would be to do the same thing to each of the items in the array. However, for large large numbers, the script would hang on the first part. For smaller numbers (like 1200000), the tests would complete so quickly as to make it nearly impossible to time to any degree of precision. That being said, the first two performed in roughly the same time (slightly less than 2 seconds) and the 3rd was faster (about a second or less)

I will try to find a better way to test the script that hopefully will not hang at the first part.

Zeriab

#7
I suggest you do a Graphics.update between each test.
The script-hanging error comes when Graphics.update isn't called after a certain amount of time.
You can just use the system time to figure out the differences in time from before and after the test. (http://www.ruby-doc.org/docs/ProgrammingRuby/html/ref_c_time.html)
Store the results of each run.

Run the test a decent amount of times. Let's just say 30 or 50. Whatever you find suitable.
Give a schema of the test results and draw conclusions out from the schema.

Good luck ^_^

Edit: Btw. would this test code be reliable? (Note: I didn't include time because you have to figure that part out yourself ;))
I use algorithms with a complexity of n2 instead of n.
class Test_Object
  attr_reader :id
  attr_reader :test_variable
  def initialize (id)
    @id = id
    @test_variable = 0
  end
  def test
    # This is just a random method call
    @test_variable += 1
  end
end


class Test
  def initialize
    test_array = Array.new(2000)
    for i in 0...test_array.size
      test_array[i] = Test_Object.new(i)
    end
    p 'Test A Begins'
    for i in 0...test_array.size
      for j in 0...test_array.size
        test_array[i].test
      end
    end
    p 'Test A Completed'
    p 'Test B begins'
    for i in test_array
      for j in test_array
        i.test
      end
    end
    p 'Test B completed'
    p 'Test C begins'
    test_array.each {|i| test_array.each{|j| i.test}}
    p 'Test C completed'
  end
end

Test.new