Hugh Sasse Staff Elec Eng <hgs / dmu.ac.uk> writes:

> Ruby already has mutiple assignment.  Would it be possible to extend
> for to allow multiple things to be selected at once?
> 
> for p1, p2, p3, result1, result2 in long_list_of_tests do
>     mine = My_class.new()
>     mine.method1(p1,p2,p3)
>     assert_equal(result1, mine.feature1())
>     assert_equal(result2, mine.feature2())
> end
> ...
> Maybe there is a good reason why this has not been allowed.

Well, I can't speak to the reason, but I can suggest why it might not
be a Good Thing.

Firstly, Ruby does allow

  for a,b,c in [[1,2,3],[4,5,6]]

The 'for' iterates over the elements in the list, which are
themselves arrays. The assignment of the array to the for loop
variables is then done in parallel.

So, why not allow the assignment to pull values from a linear array?

Two possible reasons:

1. It would be a special case, inconsistent with the semantics of
   assignment elsewhere. Remember that the for loop actually becomes
   (effectively)

      for i in list  =>  list.each do |i|

   So a for loop with multiple parameters is

      for i,j,k in list  =>  list.each do |i,j,k|

   The iterator would have to change it's behavior depending on the
   block parameter count, which would be weird.

2. It isn't very good OO style. This one is trickier. What you're
   coding up top is an array where every fifth element is a parameter, 
   every fifth element plus one is another parameter, etc.

   Wouldn't it be more natural to have a class which represented an
   individual test?

     class TestStuff
        def initialize(p1, p2, p3, res1, res2)
          #...
        end
     end


    tests = [
       TestStuff.new(1, 2, 3, 4, 5),
       TestStuff.new(5, 6, 7, 8, 9)
    ];

   for t in tests
     mine = My_class.new()
     mine.method1(t.p1, t.p2, t.p3)
     assert_equal(t.res1, mine.feature1())
     assert_equal(t.res2, mine.feature2())
   end


   You could then extend it further. TestStuff could 'know' about
   My_class,

   class TestStuff
        def initialize(p1, p2, p3, res1, res2)
          #...
        end
        def runTest
          mine = My_class.new()
          mine.method1(@p1, @p2, @p3)
          assert_equal(@res1, mine.feature1())
          assert_equal(@res2, mine.feature2())
        end
     end

   And the loop would be

   tests.each {|t| t.runTest}


   However, whether this is appropriate depends on the structure of
   your application.


Regards


Dave