Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

A little Quiz

0 views
Skip to first unread message

Dominik Bathon

unread,
Jun 6, 2005, 12:44:39 PM6/6/05
to
First of all, this is no attempt to rival with James' nice Ruby Quiz ;-)

I came up with a nice new(?) use for method_missing today.
Now it is your job to figure out what you can do with it.
(My hope is that someone will find nice uses for this, that I didn't think
of)

So here is the code:

class Array
def method_missing(meth, *args, &block)
rmeth = (meth.to_s =~ /\A_x_(.*)/ ? $1.to_sym : meth)
if block
if empty?
[]
else
mm_block_rec(rmeth, 0, res=[], [], *args, &block)
res
end
else
res=[]
each_with_index { |el, i|
res << el.send(rmeth, *(args.collect { |el|
(Array === el) ? el[i] : el
}))
}
res
end
end

private

def mm_block_rec(rmeth, i, res, bargs, *args, &block)
myargs=args.collect { |el| (Array === el) ? el[i] : el }
res[i] = self[i].send(rmeth, *myargs) { |*ba|
bargs[i]=(ba.size==1 ? ba.first : ba)
if i==size-1
block.call(*bargs)
else
mm_block_rec(rmeth, i+1, res, bargs, *args, &block)
end
}
end
end


And some questions:

What does it do?

What can it be used for?

Has this been done before? (I couldn't find anything.)

Do you like it?


Dominik


James Britt

unread,
Jun 6, 2005, 1:32:05 PM6/6/05
to
Dominik Bathon wrote:
> First of all, this is no attempt to rival with James' nice Ruby Quiz ;-)
>
> I came up with a nice new(?) use for method_missing today.
> Now it is your job to figure out what you can do with it.
> (My hope is that someone will find nice uses for this, that I didn't
> think of)
>

Interesting.

>
> And some questions:
>
> What does it do?
>
> What can it be used for?
>
> Has this been done before? (I couldn't find anything.)


Reminds me of some code I wrote a few years ago when I was poking into
method-oriented programming.

Rather than have
receiver.message( args )

I wanted to reverse things and do
message( args )->[ list_of_receivers ]


I hacked on Symbol and the result was that I could loop over a set of
objects, or use a proc to conditionally send the message to objects in
ObjectSpace (i.e., "I know what I want to do, I just don't know who to
do it to"). The results were then collected and returned as an array.

The call looked something like

:message.>>( obj_set_or_criteria_proc ) { args }

I never found a really practical application for this. Conceivably, one
could close all open file handles, or shut down lingering socket
connections, or save user sessions if the session was x minutes old, or
whatever.

It was mostly a "Gee, I wonder if ..." sort of thing, and the best
scenario I could think of was if I had an app that might need a global
shutdown, so one might want to dispatch a common set of messages across
a range of unknown objects before ending.

I never released the code, being unhappy with the syntax. But I still
like the idea of casting a message out into object space and reeling
back the results.


BTW, I tried out your code, with a trivial example:

p %w{ This is some text }.size

Ah, but, of course, method_missing never gets called, so I did not get
what I wanted.

This works, though:

p %w{ This is some text }.upcase

So there is the issue of trying to distribute a method across the list
when that method is also implemented by Array

James

--

http://www.ruby-doc.org - The Ruby Documentation Site
http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys


Dominik Bathon

unread,
Jun 6, 2005, 2:44:23 PM6/6/05
to
On Mon, 06 Jun 2005 19:32:05 +0200, James Britt <jam...@neurogami.com>
wrote:

> BTW, I tried out your code, with a trivial example:
>
> p %w{ This is some text }.size
>
> Ah, but, of course, method_missing never gets called, so I did not get
> what I wanted.

Hint: _x_ ;-)

Dominik


James Edward Gray II

unread,
Jun 6, 2005, 9:45:07 PM6/6/05
to
On Jun 6, 2005, at 11:44 AM, Dominik Bathon wrote:

> First of all, this is no attempt to rival with James' nice Ruby
> Quiz ;-)

Then you definitely should have sent it in as next week's quiz. ;)

James Edward Gray II

Logan Capaldo

unread,
Jun 7, 2005, 1:06:52 PM6/7/05
to

I've thought sometimes that this would be a good shortcut:

module Enumerable
alias __original__map__ map
def map(*args, &block)
if args.length > 1
__original__map__ { |obj| args.inject(obj) {
|o, meth_sym|

o.send(meth_sym) }
}
else
__original__map__(&block)
end
end
end

so we have [ ' hello ', ' world', ' ! '].map(:upcase, :strip)

Doesn't give you anything over the block syntax, but it is slightly less typing.


Dominik Bathon

unread,
Jun 8, 2005, 7:24:45 PM6/8/05
to
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


Dave Burt

unread,
Jun 9, 2005, 7:28:28 AM6/9/05
to
"Dominik Bathon" <dba...@gmx.de> wrote:
>
> <snip>

>
> I realize there are lots of inconsistences in this concept, but I really
> like it generally. So, what do you think?
>
> <snip backwards>

>
> [3, 3].times { |x, y|
> p [x, y]
> }

That is awesome.

Brian Schröder

unread,
Jun 9, 2005, 4:11:07 PM6/9/05
to
> [snip]

> [3, 3].times { |x, y|
> p [x, y]
> }
>
> instead of
>
> 3.times { |x|
> 3.times { |y|
> p [x, y]
> }
> }
>
> [snip]

> I realize there are lots of inconsistences in this concept, but I really
> like it generally. So, what do you think?
>

I like that second version better, because its clearer. It makes clear
in which order you are iterating. I'd even prefer something including
0 and 2 (upto, each, for) to make clear what the boundaries are.

regards,

Brian


> Dominik
>
>


--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/


0 new messages