2010-06-03 5 views
7

Tenga en cuenta el siguiente código de Perl.Perl, evalúe la cadena perezosamente

#!/usr/bin/perl 

use strict; 
use warnings; 

$b="1"; 

my $a="${b}"; 

$b="2"; 

print $a; 

La secuencia de comandos obviamente salidas 1. Me gustaría que fuera el valor actual de $b.

¿Cuál sería la forma más inteligente en Perl para lograr una evaluación perezosa como esta? Me gustaría que el ${b} permanezca "no reemplazado" hasta que se necesite $a.

+0

Sugeriría no utilizar '$ a' y' $ b' para ejemplos como este, ya que estos son especiales de perl y * no * de alcance léxico (como lo demuestra su omisión del 'mi' delante de' $ b' en tu ejemplo). – pilcrow

Respuesta

15

estoy más interesado en saber qué quiere hacer esto. Puede usar una variedad de enfoques dependiendo de lo que realmente necesite hacer.

Se podría concluir el código en un coderef, y sólo evaluarlo cuando lo necesite:

use strict; use warnings; 

my $b = '1'; 
my $a = sub { $b }; 
$b = '2'; 
print $a->(); 

Una variante de esto sería utilizar una función denominada como closure (esto es, probablemente, la mejor enfoque, en el contexto más amplio de su código de llamada):

my $b = '1'; 
sub print_b 
{ 
    print $b; 
} 

$b = '2'; 
print_b(); 

se puede utilizar una referencia a la variable original y eliminar la referencia según sea necesario:

my $b = '1'; 
my $a = \$b; 
$b = '2'; 
print $$a; 
+1

+1 para la buena selección de técnicas, pero para que funcione su segunda subrutina, debe definir 'mi $ b' antes de definir la subrutina; de lo contrario, se une al $ b global. –

+0

@Andy: ¡buen punto! – Ether

+0

Estaba tratando de encontrar una manera de hacer una substición de cuerdas rápida y sucia. Tu sugerencia coderef fue muy buena, gracias – Mike

4

Perl interpolará una cadena cuando se ejecuta el código, y no sé de qué manera hacer que no lo haga, sin formatos (que son feos IMO). Lo que podría hacer, sin embargo, es el cambio "cuando se ejecuta el código" a algo más conveniente, envolviendo la cadena en un sub y decir que es cuando se necesita la cadena interpolada ...

$b = "1"; 
my $a = sub { "\$b is $b" }; 
$b = "2"; 
print &$a; 

O bien, puede hacer algo de magia de eval, pero es un poco más intrusivo (tendrías que hacer algo de manipulación de la cuerda para lograrlo).

1

Desea fingir que $ a se refiere a algo que se evalúa cuando se usa $ a ... Solo puede hacer eso si $ a no es realmente un escalar, podría ser una función (como la respuesta de cHao) o , en este caso simple, una referencia a la otra variable

my $b="1"; 
my $a= \$b; 
$b="2"; 
print $$a; 
3

Lo que desea no es una evaluación perezosa, sino encuadernación tardía. Para obtenerlo en Perl, necesita usar eval.

my $number = 3; 
my $val = ""; 

my $x = '$val="${number}"'; 

$number = 42; 

eval $x; 

print "val is now $val\n"; 

tenga en cuenta que eval suele ser poco eficiente, así como metódicamente atroz. Es casi seguro que es mejor utilizar una solución de una de las otras respuestas.

3

Como han mencionado otros, Perl solo evaluará cadenas como las ha escrito utilizando eval para invocar el compilador en tiempo de ejecución. Puede usar referencias como se señala en algunas otras respuestas, pero eso cambia la apariencia del código ($$a vs $a). Sin embargo, al ser Perl, hay una manera de ocultar la funcionalidad avanzada detrás de una variable simple, usando tie.

{package Lazy; 
    sub TIESCALAR {bless \$_[1]}   # store a reference to $b 
    sub FETCH {${$_[0]}}     # dereference $b 
    sub STORE {${$_[0]} = $_[1]}   # dereference $b and assign to it 
    sub new {tie $_[1] => $_[0], $_[2]} # syntactic sugar 
} 

my $b = 1; 
Lazy->new(my $a => $b); # '=>' or ',' but not '=' 

print "$a\n"; # prints 1 
$b = 2; 
print "$a\n"; # prints 2 

Usted puede buscar la documentación de tie, pero en pocas palabras, que le permite definir su propia implementación de una variable (por escalares, arrays, hashes, o identificadores de archivo).Por lo tanto, este código crea la nueva variable $a con una implementación que obtiene o establece el valor actual de $b (almacenando una referencia a $b internamente). El método new no es estrictamente necesario (el constructor es realmente TIESCALAR) pero se proporciona como azúcar sintáctico para evitar tener que usar tie directamente en el código de llamada.

(que sería tie my $a, 'Lazy', $b;)

1

Me gustaría que el $ {b} para permanecer "no reemplazada" hasta que se necesita $ a.

Entonces recomendaría evitar la interpolación de cadenas, en su lugar usando sprintf, para que pueda "interpolar" cuando sea necesario.

Por supuesto, sobre esta base se podría tie juntos algo rápido (más o menos) y sucio:

use strict; 
use warnings; 

package LazySprintf; 

# oh, yuck 
sub TIESCALAR { my $class = shift; bless \@_, $class; } 
sub FETCH  { my $self = shift; sprintf $self->[0], @$self[1..$#$self]; } 

package main; 

my $var = "foo"; 
tie my $lazy, 'LazySprintf', '%s', $var; 

print "$lazy\n"; # prints "foo\n" 
$var = "bar"; 
print "$lazy\n"; # prints "bar\n"; 

Obras con especificadores de formato más exóticos, también. Yuck.

+0

Totally awesome 'yuck' :-) –

Cuestiones relacionadas