From: Dominik Bathon Date: 2005-06-09T08:24:45+09:00 Subject: Re: A little Quiz (Solution) Obviously no one really likes to decipher undocumented code... (I should have known after the "Code Cleaning"-Quiz ;-) Well, so let me tell you what it does: As James Britt already figured out, it lets you call one method on all elements of an Array. If no block is given this works like collect. Instead of [-1, -2, -3].collect { |x| x.abs } => [1, 2, 3] you can just write [-1, -2, -3].abs => [1, 2, 3] Since #abs is not part of the Array interface, method_missing is triggered. But it does more than collect, it even works on nested Arrays: [[-1,-2],[-3,4],-2,5].abs => [[1, 2], [3, 4], 2, 5] If you try a method that is part of Array's interface, method_missing obviously won't be called: %w[abc de fghi].size => 3 But there is a trick (maybe hack): %w[abc de fghi]._x_size => [3, 2, 4] The optional "_x_" is automatically stripped. But this also has a downside, it doesn't work with nested arrays, but on the other hand you can choose an explicit depth: [[[1,2,3], "iuhiu"], ["iu", [4,5,6]]]._x__x_size => [[3, 5], [2, 3]] But methods also can get arguments, so how are they handled? Some examples: %w[abc de fgh ijk].ljust(5) => ["abc ", "de ", "fgh ", "ijk "] %w[abc de fgh ijk].ljust((4..8).to_a) => ["abc ", "de ", "fgh ", "ijk "] %w[abc de fgh ijk].ljust(5, %w[1 2 3 4]) => ["abc11", "de222", "fgh33", "ijk44"] %w[abc de fgh ijk].ljust([5, 3, 5, 6], %w[1 2 3 4]) => ["abc11", "de2", "fgh33", "ijk444"] I think you can get the idea. But all this was just the beginning ;-) My motivation to write this actually was to be able to say [3, 3].times { |x, y| p [x, y] } instead of 3.times { |x| 3.times { |y| p [x, y] } } Well, now I can. And of course you can combine this with the above: [1,2].upto(4) { |*a| p a } output: [1, 2] [1, 3] [1, 4] [2, 2] [2, 3] [2, 4] [3, 2] [3, 3] [3, 4] [4, 2] [4, 3] [4, 4] [1,2].upto([2, 3]) { |*a| p a } output: [1, 2] [1, 3] [2, 2] [2, 3] [%w[a b c], [1, 2, 3]]._x_each { |*a| p a } output: ["a", 1] ["a", 2] ["a", 3] ["b", 1] ["b", 2] ["b", 3] ["c", 1] ["c", 2] ["c", 3] If multiple arguments would be yielded to the blocks, they are grouped by arrays: [%w[a b c], 1..3]._x_each_with_index { |*a| p a } output: [["a", 0], [1, 0]] [["a", 0], [2, 1]] [["a", 0], [3, 2]] [["b", 1], [1, 0]] [["b", 1], [2, 1]] [["b", 1], [3, 2]] [["c", 2], [1, 0]] [["c", 2], [2, 1]] [["c", 2], [3, 2]] Because I wanted the multidimensional iteration, it doesn't work nice with things like collect: [%w[a b], 1..2]._x_collect { |*a| a } => [[[["a", 1], ["a", 2]], [["b", 1], ["b", 2]]], [["b", 1], ["b", 2]]] Maybe I should add another special prefix ( ;-) ) so that it works more like the non block version. Maybe like this: [%w[a b], 1..2]._y_collect { |*a| a+a } => [["aa", "bb"], [2, 4]] Or maybe use _i_ for the multidimensional iteration and _x_ for the rest... I also thougt about adding it to Enumerable, but I think it doesn't make to much sense for other Enumerables (like Ranges) and if you really need it you can always use #to_a. I realize there are lots of inconsistences in this concept, but I really like it generally. So, what do you think? Dominik