2012-08-15 13 views
5

Primer temporizador ... así que avíseme si hay algo que no haya prestado atención mientras planteaba una pregunta.Usar un escalar como condición en perl

La pregunta es cómo usar un escalar como condición, ya que el siguiente código no funciona.

my @parameter=('hub'); 

my %condition; 
$condition{'hub'}{'1'}='$degree>=5'; 

foreach (@parameter) { 
     if ($condition{$_}{'1'}) {..} 
} 

Pensé que es porque la condición no se interpreta correctamente, por lo que también intenté lo siguiente, que tampoco funcionó.

if ("$condition{$parameter}{'1'}") { ..} 

Realmente agradecería cualquier ayuda. :)

+0

te refieres $ condition {'hub'} {'1'} = '$ degree => 5'; ? – squiguy

Respuesta

11

O se quieren eval cadena, que evalúa una cadena como código Perl

if (eval $condition{$_}{'1'}) { ... 

o tal vez un enfoque más seguro estaría utilizando code references

$condition{'hub'}{'1'} = sub { return $degree>=5 }; 

if ($condition{$_}{'1'}->()) { ... 

En el segundo ejemplo, está adjuntando un fragmento de código a una variable. La sintaxis $var->() ejecuta el código y evalúa el valor de retorno del código.

+3

Los subs anónimos son la forma * correcta * de hacerlo. :) – friedo

+0

eval works. $ condition {'hub'} {'1'} = sub {return $ degree> = 5}; no funciona para mí porque las variables deben definirse para que eso funcione; mis variables se definen más adelante en el código. Gracias :) – bioinformant

2

¿Qué estás esperando? Los valores de cadena se interpretan como true cuando no están vacíos.

[email protected]: ~ $ perl -e 'print "oops\n" if "false" ; ' 
oops 
[email protected]: ~ $ perl -e 'print "oops\n" if "" ; ' 
[email protected]: ~ $ perl -e 'print "oops\n" if "\$degree < 5" ;' 
oops 

Si desea evaluar dinámicamente código en sus condiciones, que tienen que investigar eval. Ejemplo:

my @conds=('$foo>42', '$foo>23'); 
my $foo = 33; 

foreach my $cond(@conds) { 
    print "$cond itself was true\n" if $cond; 
    print "$cond evaluated to true\n" if eval($cond); 
} 

impresiones

$foo>42 itself was true 
$foo>23 itself was true 
$foo>23 evaluated to true 
+2

'eval' es la forma incorrecta de hacer esto. – friedo

+0

Sí, almacenar código en cadenas es malo. – themel

+1

Aunque se recomienda una advertencia sobre 'eval', no siempre es" la forma incorrecta ". A veces, la forma más rápida y fácil está bien. No todos los guiones de Perl van a manejar el soporte vital en la estación espacial o guiar el bisturí de un cirujano robótico. – dan1111

4

Lo que intenta hacer es evaluar '$ degree> = 5' como código real. En lugar de tratar de evaluar la cadena como código (que se puede hacer con eval), generalmente es más seguro y, a menudo más robusto, en cambio pasa una referencia de código. Se puede utilizar una subrutina generador para generar submarinos condicionadas a la demanda, así:

sub generate_condition { 
    my ($test, $bound) = @_; 
    return sub { return $test >= $bound; }; 
} 

my %condition; 
$condition{'hub'}{'1'} = generate_condition($degree, 5); 

if($condition{$parameter}{1}->()) { ... } 

Se pone un poco más complicado si desea que el >= (es decir, la relación en sí) que se crea de forma dinámica también. Entonces tienes un par de opciones. Uno lo lleva de vuelta a la evaluación fibrosa, con todos sus riesgos (especialmente si comienza a permitir que su usuario especifique la cadena). El otro sería una tabla de búsqueda dentro de su sub generate_condition().

generate_condition() devuelve una referencia de subrutina que, cuando se invoca, evaluará la condición que estaba vinculada en el momento de la creación.

Aquí hay una solución generalizada que aceptará cualquiera de los condicionales de Perl y los envolverá junto con los argumentos probados en una subrutina. El subref continuación, se puede invocar para evaluar el condicional:

use strict; 
use warnings; 
use feature qw/state/; 

sub generate_condition { 
    my ($test, $relation, $bound) = @_; 
    die "Bad relationship\n" 
     if ! $relation =~ m/^(?:<=?|>=?|==|l[te]|g[te]|cmp)$/; 
    state $relationships = { 
     '<'  => sub { return $test < $bound }, 
     '<=' => sub { return $test <= $bound }, 
     '==' => sub { return $test == $bound }, 
     '>=' => sub { return $test >= $bound }, 
     '>'  => sub { return $test > $bound }, 
     '<=>' => sub { return $test <=> $bound }, 
     'lt' => sub { return $test lt $bound }, 
     'le' => sub { return $test le $bound }, 
     'eq' => sub { return $test eq $bound }, 
     'ge' => sub { return $test ge $bound }, 
     'gt' => sub { return $test gt $bound }, 
     'cmp' => sub { return $test cmp $bound }, 
    }; 
    return $relationships->{$relation}; 
} 


my $true_condition = generate_condition(10, '>', 5); 
my $false_condition = generate_condition('flower', 'eq', 'stamp'); 

print '10 is greater than 5: ', 
     $true_condition->() ? "true\n" : "false\n"; 
print '"flower" is equal to "stamp": ', 
     $false_condition->() ? "true\n" : "false\n"; 

A menudo, cuando se construye este tipo de cosas que uno está interesado en dejar un parámetro abierta para unirse a las llamadas en tiempo en lugar de en la producción subrutina tiempo.Supongamos que solo desea vincular los parámetros "$bound" y "$ relation", pero deje "$test" abierto para la especificación en el momento de la llamada a la subrutina. Se podría modificar su sub generación de la siguiente manera:

sub generate_condition { 
    my ($relation, $bound) = @_; 
    die "Bad relationship\n" 
     if ! $relation =~ m/^(?:<=?|>=?|==|l[te]|g[te]|cmp)$/; 
    state $relationships = { 
     '<'  => sub { return $_[0] < $bound }, 
     # ...... 

e invocarla como esto:

my $condition = generate_condition('<', 5); 
if($condition->(2)) { 
    print "Yes, 2 is less than 5\n"; 
} 

Si el objetivo es proporcionar el enlace en tiempo tanto de la izquierda y el lado derecho en la evaluación relacional, esto va a funcionar:

sub generate_condition { 
    my $relation = shift; 
    die "Bad relationship\n" 
     if ! $relation =~ m/^(?:<=?|>=?|==|l[te]|g[te]|cmp)$/; 
    state $relationships = { 
     '<'  => sub { return $_[0] < $_[1] }, 
     '<=' => sub { return $_[0] <= $_[1] }, 
     # ...... and so on ..... 
    return $relationship->($relation); 
} 

my $condition = generate_condition('<'); 
if($condition->(2,10)) { print "True.\n"; } 

este tipo de herramienta entra en la categoría de programación funcional, y está cubierta con profusión de detalles en el libro de MarkJason Dominus

+0

Su primer bloque de código tiene problemas: http://pastie.org/4515233 - debería usar \ $ degree/$$ test. – themel

+0

Lo estás usando mal. Se supone que no debe mirar $ degree después de que se fabrica el submarino. Está pensado para bloquear $ degree y $ test en el momento en que se genera la condición sub. Si desea actualizar $ grado, necesita generar un nuevo sub (con nuevos valores vinculados), o usar el segundo fragmento de código que permite que el valor de prueba permanezca libre hasta el momento en que se invoque el sub anónimo. – DavidO

+0

Eh, está bien, pero parece un poco inútil. ¿Por qué evaluar la condición cada vez que el valor de retorno es constante? – themel

Cuestiones relacionadas