[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Implicit block parameter?

Ross Bamford

1/2/2006 8:21:00 PM

Hi,

Probably this has been considered before, but I'll ask anyway.

Before I used Ruby, I used Groovy. In some ways they are (well, were
anyway) quite similar, and one feature that I found cool in Groovy that I
often miss in Ruby is the implicit (/default/whatever?) block parameter,
'it':

[1,2,3].each { puts it }

AFAIR this was only provided when no 'it' existed in scope, and the block
had a single argument passed when none were declared. Maybe this would
interfere with Ruby's warnings about block parameter mismatch, or maybe
the implementation doesn't allow for it, but I just wondered if it might
be possible, because I notice I do:

[1,2,3].each { |it| puts it }

and it bugs me a little bit :D

I did try hacking it from the Ruby side and came up with a halfway
solution using instance variables and the Binding extension from Rubyforge:

require 'rubygems'
require 'extensions/binding'

def itproc(&blk)
class << blk
def [](*args)
if args.length == 1
begin
old = binding[:@it]
binding[:@it] = args[0]
super
ensure
binding[:@it] = old
end
else
super
end
end

alias :call :[]
end
blk
end

But of course this doesn't work with regular procs (doing it on Proc
causes a segfault here, I guess because extensions uses procs itself to do
the binding stuff?) and of course it doesn't happen with yielded blocks,
even when passed from procs:

pr = itproc { puts "@it = #{@it.inspect}" }
pr2 = itproc { |one| puts "@it = #{@it.inspect}; one = #{one.inspect}" }
pr3 = itproc { |a, b| puts "@it = #{@it.inspect}; a = #{a.inspect}; b =
#{b}" }

# Works
puts "Call"
pr.call('Hello')
pr2.call('Hello')
pr3.call('He','llo')

# Works
puts "\n[]"
pr['Bye']
pr2['Bye']
pr3['Bye','Bye']

# Doesn't work through yield though :(
puts "\nYield"
[1,2,3].each &pr
[1,2,3].each &pr2
[1,2,3].each &pr3

Anyway, it's a bit of an abuse of instance vars I guess, and obviously
doesn't do the job properly - I wonder if anyone else has thought about
this, and whether it's something that doesn't already exist in Ruby itself
for a reason?

Cheers,

--
Ross Bamford - rosco@roscopeco.remove.co.uk
30 Answers

Ross Bamford

1/2/2006 9:31:00 PM

0

On Mon, 02 Jan 2006 20:20:30 -0000, I wrote:

> Before I used Ruby, I used Groovy. In some ways they are (well, were
> anyway) quite similar, and one feature that I found cool in Groovy that
> I often miss in Ruby is the implicit (/default/whatever?) block
> parameter, 'it':
>
> [1,2,3].each { puts it }
>
> [...]
>
> I did try hacking it from the Ruby side and came up with a halfway
> solution using instance variables and the Binding extension from
> Rubyforge:
>

I got a bit closer by defining an attr_reader 'it' on Kernel, but still it
doesn't work with all Procs or via Yield...

--
Ross Bamford - rosco@roscopeco.remove.co.uk

Simon Strandgaard

1/2/2006 10:04:00 PM

0

On 1/2/06, Ross Bamford <rosco@roscopeco.remove.co.uk> wrote:
> On Mon, 02 Jan 2006 20:20:30 -0000, I wrote:
>
> > Before I used Ruby, I used Groovy. In some ways they are (well, were
> > anyway) quite similar, and one feature that I found cool in Groovy that
> > I often miss in Ruby is the implicit (/default/whatever?) block
> > parameter, 'it':
> >
> > [1,2,3].each { puts it }

how about?

class Array
alias :each1 :each
def each(&b)
each1{|i| b.call($i = i) }
end
end
%w(a b c).each{p $i} #-> "a" "b" "c"


--
Simon Strandgaard


Simon Strandgaard

1/2/2006 10:07:00 PM

0

On 1/2/06, Simon Strandgaard <neoneye@gmail.com> wrote:
> On 1/2/06, Ross Bamford <rosco@roscopeco.remove.co.uk> wrote:
> > On Mon, 02 Jan 2006 20:20:30 -0000, I wrote:
> >
> > > Before I used Ruby, I used Groovy. In some ways they are (well, were
> > > anyway) quite similar, and one feature that I found cool in Groovy that
> > > I often miss in Ruby is the implicit (/default/whatever?) block
> > > parameter, 'it':
> > >
> > > [1,2,3].each { puts it }
>
or how about?

module Kernel
def it
$i
end
end
class Array
alias :each1 :each
def each(&b)
each1{|i| b.call($i = i) }
end
end

[1, 2, 3].each{ puts it } # 1 2 3



--
Simon Strandgaard


Doug H

1/2/2006 10:27:00 PM

0

I think he wants this for any single parameter block though, not just
arrays.

What about automatically creating aliases, too? Like where you put
"alias :each1 :each", have it automatically create an alias
":old_methodname" whenever you overwrite an existing method (replace
"methodname" with actual name").
.

Dominik Bathon

1/2/2006 10:55:00 PM

0

On Mon, 02 Jan 2006 21:32:57 +0100, Ross Bamford
<rosco@roscopeco.remove.co.uk> wrote:

> Hi,
>
> Probably this has been considered before, but I'll ask anyway.

I have also proposed this in October:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-t...

And it seems to have been proposed before.

I have actually implemented it for ruby 1.9 lately (it should work similar
for 1.8). It's just a small patch for eval.c:

--- eval_org.c 2005-12-30 02:17:49.000000000 +0100
+++ eval.c 2006-01-02 23:25:46.000000000 +0100
@@ -4656,6 +4656,7 @@ rb_yield_0(VALUE val, VALUE self, VALUE
NODE *cnode = ruby_current_node;
int lambda = flags & YIELD_LAMBDA_CALL;
int state;
+ VALUE it_val = Qnil;

rb_need_block();

@@ -4675,8 +4676,10 @@ rb_yield_0(VALUE val, VALUE self, VALUE
scope_vmode = (flags & YIELD_PUBLIC_DEF) ? SCOPE_PUBLIC :
block->vmode;
ruby_block = block->prev;
if (block->flags & BLOCK_D_SCOPE) {
+ if (avalue && RARRAY(val)->len != 0) it_val = RARRAY(val)->ptr[0];
+ if (!avalue && val != Qundef) it_val = val;
/* put place holder for dynamic (in-block) local variables */
- ruby_dyna_vars = new_dvar(0, 0, block->dyna_vars);
+ ruby_dyna_vars = new_dvar(0, it_val, block->dyna_vars);
}
else {
/* FOR does not introduce new scope */
@@ -7596,6 +7599,15 @@ rb_exec_end_proc(void)
ruby_safe_level = safe;
}

+static VALUE
+rb_f_it(void)
+{
+ if (ruby_dyna_vars) {
+ if (ruby_dyna_vars->id == 0) return ruby_dyna_vars->val;
+ }
+ return Qnil;
+}
+
void
Init_eval(void)
{
@@ -7650,6 +7662,8 @@ Init_eval(void)
rb_define_global_function("global_variables", rb_f_global_variables,
0); /* in variable.c */
rb_define_global_function("local_variables", rb_f_local_variables, 0);

+ rb_define_global_function("it", rb_f_it, 0);
+
rb_define_method(rb_mKernel, "send", rb_f_send, -1);
rb_define_method(rb_mKernel, "__send__", rb_f_send, -1);
rb_define_method(rb_mKernel, "funcall", rb_f_funcall, -1);


This defines a global function #it that always returns the first block
argument, it is not assignable. #it is available whether other block
parameters are there or not. #it defaults to nil (if no arguments are
given or if not in a block context.

$ cat test_it.rb
p (1..5).to_a.map { -it }

%w[a b c].each { |x| p [x, it] }

p it
$ ./miniruby test_it.rb
[-1, -2, -3, -4, -5]
["a", "a"]
["b", "b"]
["c", "c"]
nil


Dominik


Ross Bamford

1/3/2006 12:29:00 AM

0

On Mon, 02 Jan 2006 22:06:49 -0000, Simon Strandgaard <neoneye@gmail.com>
wrote:

> On 1/2/06, Simon Strandgaard <neoneye@gmail.com> wrote:
>> On 1/2/06, Ross Bamford <rosco@roscopeco.remove.co.uk> wrote:
>> > On Mon, 02 Jan 2006 20:20:30 -0000, I wrote:
>> >
>> > > Before I used Ruby, I used Groovy. In some ways they are (well, were
>> > > anyway) quite similar, and one feature that I found cool in Groovy
>> that
>> > > I often miss in Ruby is the implicit (/default/whatever?) block
>> > > parameter, 'it':
>> > >
>> > > [1,2,3].each { puts it }
>>
> or how about?
>
> module Kernel
> def it
> $i
> end
> end
> class Array
> alias :each1 :each
> def each(&b)
> each1{|i| b.call($i = i) }
> end
> end
>
> [1, 2, 3].each{ puts it } # 1 2 3
>

Cool, didn't consider doing it that way :) I was thinking in more general
terms, but this way does get it working with each. I did notice, though,
that it doesn't seem too happy with other Enumerable methods (select and
collect seem pretty odd)...

Cheers,

--
Ross Bamford - rosco@roscopeco.remove.co.uk

Ross Bamford

1/3/2006 12:34:00 AM

0

On Mon, 02 Jan 2006 22:26:46 -0000, Doug H <doug00@gmail.com> wrote:

> What about automatically creating aliases, too? Like where you put
> "alias :each1 :each", have it automatically create an alias
> ":old_methodname" whenever you overwrite an existing method (replace
> "methodname" with actual name").
> .
>

That would be nice, but I wonder what'd happen if a method was aliased
more than once. The alias is another bit of noise, and doesn't protect you
from manually overwriting a previous alias, but at least you get the
chance to fix it.

Cheers,

--
Ross Bamford - rosco@roscopeco.remove.co.uk

dblack

1/3/2006 2:33:00 AM

0

pere.noel

1/3/2006 8:31:00 AM

0

Simon Strandgaard <neoneye@gmail.com> wrote:

> class Array
> alias :each1 :each
> def each(&b)
> each1{|i| b.call($i = i) }
> end
> end
> %w(a b c).each{p $i} #-> "a" "b" "c"


could I use this mechanisms for String ?

actually i've a function call :

s="Happy new year!"
s_utf8=to_UTF8(s)
p s_utf8

with :

def to_UTF8(s)
return Iconv.new('UTF-8', 'MacRoman').iconv(s)
end

something like :

class String
def to_utf8(s)
return Iconv.new('UTF-8', 'MacRoman').iconv(s)
end
end

p "Happy new year!".to_UTF8

does that make sense ?
--
une bévue

Ross Bamford

1/3/2006 9:34:00 AM

0

On Mon, 02 Jan 2006 22:54:43 -0000, Dominik Bathon <dbatml@gmx.de> wrote:

> On Mon, 02 Jan 2006 21:32:57 +0100, Ross Bamford
> <rosco@roscopeco.remove.co.uk> wrote:
>
>> Hi,
>>
>> Probably this has been considered before, but I'll ask anyway.
>
> I have also proposed this in October:
>
> http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-t...
>
> And it seems to have been proposed before.
>
> I have actually implemented it for ruby 1.9 lately (it should work
> similar for 1.8). It's just a small patch for eval.c:
>
> ... [snipped patch] ...

Wicked :) I can confirm that seems to work in 1.8 (though I had to make
one change manually because the source is slightly different of course). I
guessed it'd be something that would be reasonably easy (when you know how
;)) from the C side.

FWIW +1 for inclusion in a future Ruby.

Cheers,

--
Ross Bamford - rosco@roscopeco.remove.co.uk