A little Quiz

D

Dominik Bathon

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 : el
}))
}
res
end
end

private

def mm_block_rec(rmeth, i, res, bargs, *args, &block)
myargs=args.collect { |el| (Array === el) ? el : el }
res = self.send(rmeth, *myargs) { |*ba|
bargs=(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
 
J

James Britt

Dominik said:
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
 
D

Dominik Bathon

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
 
J

James Edward Gray II

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
 
L

Logan Capaldo

Dominik said:
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)
=20
Interesting.
=20

And some questions:

What does it do?

What can it be used for?

Has this been done before? (I couldn't find anything.)
=20
=20
Reminds me of some code I wrote a few years ago when I was poking into
method-oriented programming.
=20
Rather than have
receiver.message( args )
=20
I wanted to reverse things and do
message( args )->[ list_of_receivers ]
=20
=20
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.
=20
The call looked something like
=20
:message.>>( obj_set_or_criteria_proc ) { args }
=20
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.
=20
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.
=20
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.
=20
=20
BTW, I tried out your code, with a trivial example:
=20
p %w{ This is some text }.size
=20
Ah, but, of course, method_missing never gets called, so I did not get
what I wanted.
=20
This works, though:
=20
p %w{ This is some text }.upcase
=20
So there is the issue of trying to distribute a method across the list
when that method is also implemented by Array
=20
=20
=20
James
=20
--
=20
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
=20
=20

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|
=20
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 ty=
ping.
 
D

Dominik Bathon

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
 
D

Dave Burt

Dominik Bathon said:
<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.
 
B

Brian Schröder

[snip]
[3, 3].times { |x, y|
p [x, y]
}
=20
instead of
=20
3.times { |x|
3.times { |y|
p [x, y]
}
}
=20
[snip]
I realize there are lots of inconsistences in this concept, but I really
like it generally. So, what do you think?
=20

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
=20
=20


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

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,173
Messages
2,570,937
Members
47,481
Latest member
ElviraDoug

Latest Threads

Top