[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Emulate perls local

Karsten Meier

9/8/2003 8:13:00 PM

Hello Ruby Fans,

I'm working on the ruby section of the pleac project,
(http://pleac.sourc...) because I think a good way to learn a new
language is trying to explain it to somebody else.
There are a few hundred code snippnets from the famous perl cookbook,
and one of it is perls local function:
You can give a global variable inside a block a new value. This is
slightly different from normal local variable, because it really changes
the global variable. I think it is sometimes even useful, when you need
to change the predefined global variables, but want to automatically
restore them after you are done.
The phyton documenter said that it is not possible in phyton.
But this is ruby, so there should be a solution.
I came up with the following:

def local(var)
eval("save = #{var.id2name}")
result = yield
eval("#{var.id2name} = save")
result
end

# example usage

def print_age
puts "Age is #{$age}"
end

$age = "to small"
print_age()
local(:$age){
$age = 23
print_age()
}
print_age()

It prints the following:
to small
23
to small

It works quite well, but I like to know if I can get rid of the eval().
I already have my variable as symbol, so I think it must be possible
somehow to restore the variable from the symbol, without use of eval.
Do you have an idea for a more elegant solution?

Regards
Karsten Meier
Hamburg * Germany

4 Answers

Rudolf Polzer

9/8/2003 9:00:00 PM

0

Scripsit illa aut ille &#187;Karsten Meier&#171; <discussion@internetlabor.de>:
> I''m working on the ruby section of the pleac project,
> (http://pleac.sourc...) because I think a good way to learn a new
> language is trying to explain it to somebody else.
> There are a few hundred code snippnets from the famous perl cookbook,
> and one of it is perls local function:
> You can give a global variable inside a block a new value. This is
> slightly different from normal local variable, because it really changes
> the global variable. I think it is sometimes even useful, when you need
> to change the predefined global variables, but want to automatically
> restore them after you are done.

Not really.

rpolzer@katsuragi ~ $ perl -e ''
$a = 17;
$p = \$a;
{
local $a = 42;
$q = \$a;
}
print "$a, $p<$$p>, $q<$$q>\n";
''
17, SCALAR(0x812e798)<17>, SCALAR(0x812155c)<42>

It''s more than Save&Restore of the variable contents. It saves and
restores the variable pointers in the symbol table. In fact, both
$a were DIFFERENT variables, which justifies the "declaration-style
syntax" of local.

> def local(var)
> eval("save = #{var.id2name}")
> result = yield
> eval("#{var.id2name} = save")
> result
> end
[...]
> It works quite well, but I like to know if I can get rid of the eval().
> I already have my variable as symbol, so I think it must be possible
> somehow to restore the variable from the symbol, without use of eval.
> Do you have an idea for a more elegant solution?

I cannot solve THAT problem, but there''s another thing:

def local(var)
eval("save = #{var.id2name}")
begin
result = yield
ensure
eval("#{var.id2name} = save")
end
end

Otherwise it doesn''t work through exceptions.

What do you need it for? If you don''t need to use the $something
variables in the usual way, you could use something like this:

class SaveRestoreVariable < Array
attr_accessor :value
def initialize(val)
@value = val
end
def localize(&block)
push(@value)
begin
yield
ensure
@value = pop()
end
end
end

v = SaveRestoreVariable.new(17)
v.localize() do
v.value = 42
puts v.value
end
puts v.value

Robert Klemme

9/9/2003 8:31:00 AM

0


"Rudolf Polzer" <denshimeiru-sapmctacher@durchnull.de> schrieb im
Newsbeitrag
news:slrnblprei.b2n.denshimeiru-sapmctacher@message-id.durchnull.ath.cx...
> Scripsit illa aut ille &#187;Karsten Meier&#171; <discussion@internetlabor.de>:
> > I''m working on the ruby section of the pleac project,
> > (http://pleac.sourc...) because I think a good way to learn a
new
> > language is trying to explain it to somebody else.
> > There are a few hundred code snippnets from the famous perl cookbook,
> > and one of it is perls local function:
> > You can give a global variable inside a block a new value. This is
> > slightly different from normal local variable, because it really
changes
> > the global variable. I think it is sometimes even useful, when you
need
> > to change the predefined global variables, but want to automatically
> > restore them after you are done.
>
> Not really.
>
> rpolzer@katsuragi ~ $ perl -e ''
> $a = 17;
> $p = \$a;
> {
> local $a = 42;
> $q = \$a;
> }
> print "$a, $p<$$p>, $q<$$q>\n";
> ''
> 17, SCALAR(0x812e798)<17>, SCALAR(0x812155c)<42>
>
> It''s more than Save&Restore of the variable contents. It saves and
> restores the variable pointers in the symbol table. In fact, both
> $a were DIFFERENT variables, which justifies the "declaration-style
> syntax" of local.

But perl and ruby treat variables differently. Especially in perl there
can be two instances that carry the value 42 while in ruby this is always
one instance that is referenced. If you would use the proposed solution
and assign another global from the temporarily changed global you could
produce similar output to your perl variant. (Without the hex addresses,
of course)

My concern would rather be that the proposed solution (apart from the
exception thingy that you fixed) would not work for multiple threads. As
far as I remember the perl''s "local" does dynamically scoping, i.e., it
affects only methods invoked from the method that contains the "local". In
a single threaded environment this is no difference, but in MT
environments as ruby this does make a significant difference. Thus a
thread safe ruby solution should rather include Thread[] and Thread[]=,
which unfortunately would make the syntax more ugly.

Technically this could be a solution, but it does not look nice which
certainly defys the original intention:

def local(sym, val)
ar = ( Thread.current[sym] ||= [] )
ar.unshift( val )

begin
yield
ensure
ar.shift
end
end

def get_local(sym)
ar = Thread.current[sym]
raise "#{sym} undefined" unless ar && !ar.empty?
ar[0]
end

def set_local(sym, val)
ar = Thread.current[sym]
raise "#{sym} undefined" unless ar && !ar.empty?
ar[0]= val
end

# usage, thread safe
local :foo, 20 do
puts get_local( :foo )

local :foo, 42 do
puts get_local( :foo )
end
end

Regards

robert

Karsten Meier

9/9/2003 7:54:00 PM

0

Thank you for the feedback.
(Are only germans interested in this kind of stuff?)
I think most usages in perl just don''t make sense in ruby,
normally it is better to use a local variable, you also have no trouble
to put file handles in a variable in ruby.
It might be useful for the predefined global variables, like
$/ input record separator
$\ output record separator
The perl cookbook mentions also it is possible to use it for temporary
signal handlers, like this (perl code)
local $SIG(INT) = ''IGNORE'';
I never needed to install a signal handler at all, so I''m not sure
if this something intersting, and if it apply to ruby.


The exception hint is very good, because I think such a local
function is only useful if the control flow is quite complex.
Otherwise it easier just to save the var and restore it later.

It is true that we have problems if we are dealing with threads,
but I think if you are using threads and manipulate globals at the same
time, you have trouble anyway. The code is correct in the sense that
the global has temporarly a new variable, so all called methods sees
this new value, and also all other threads see this variable.
but it might not be what you expected.

Karsten Meier

Rudolf Polzer

9/9/2003 9:07:00 PM

0

Scripsit illa aut ille &#187;Karsten Meier&#171; <discussion@internetlabor.de>:
> Thank you for the feedback.
> (Are only germans interested in this kind of stuff?)

No idea.

> I think most usages in perl just don''t make sense in ruby,
> normally it is better to use a local variable, you also have no trouble
> to put file handles in a variable in ruby.

Neither do you in current Perl versions:

open my $fh, "</some/file" or die ''hard'';

[...]
> The perl cookbook mentions also it is possible to use it for temporary
> signal handlers, like this (perl code)
> local $SIG(INT) = ''IGNORE'';
> I never needed to install a signal handler at all, so I''m not sure
> if this something intersting, and if it apply to ruby.

In Ruby one would just use exceptions for this purpose. Hm...
nearly, since there is no "ON ERROR RESUME NEXT".

Seems like it''s impossible to use a block for both the signal
handler and the code which is to be executed.

Hm... wouldn''t something like this (but also supporting Proc#yield
and Proc#[] which is trivial to add) be a nice extension?

class Proc
alias :origCall :call
def trap(signal, &handler)
(@traps ||= []) << [signal, handler]
self
end
def do_trapping(&block)
savedtraps = []
(@traps || []).each() do |t|
savedtraps << [t[0], Kernel.trap(t[0], t[1])]
end
begin
block.origCall() # do NOT call yield() here (endless recursion)!
ensure
savedtraps.each() do |t|
Kernel.trap(t[0], t[1])
end
end
end
def call(*args)
do_trapping do
origCall(*args)
end
end
end

trap(''INT'') do
puts "Aiiieee!!!1"
end
proc do
puts "AAA"
sleep 5
puts "BBB"
end.trap(''INT'') do
puts "SIGINT caught"
end.trap(''QUIT'') do
puts "SIGQUIT caught"
end.call() # <-- this MUST NOT be left out
sleep 5