2012-08-15 6 views
5

Erste Timer ... also lassen Sie mich wissen, wenn es etwas gibt, das ich nicht beachtet habe, während ich eine Frage gestellt habe.Verwenden eines Skalars als Bedingung in Perl

Die Frage ist, wie man einen Skalar als Bedingung verwendet, da der folgende Code nicht funktioniert.

my @parameter=('hub'); 

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

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

Ich dachte, das ist, weil die Bedingung nicht richtig interpretiert wird, also habe ich auch Folgendes versucht, was auch nicht funktioniert hat.

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

Würde wirklich jede Hilfe zu schätzen wissen. :)

+0

meinst du $ condition {'hub'} {'1'} = '$ degree => 5'; ? – squiguy

Antwort

11

Sie entweder String eval wollen, die

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

oder vielleicht einen String als Perl-Code wertet ein sicherer Ansatz code references

Verwendung würde

Im zweiten Beispiel fügen Sie einen Code an eine Variable an. Die Syntax $var->() führt den Code aus und berechnet den Rückgabewert des Codes.

+3

Anonyme Subs sind die * richtige * Möglichkeit, dies zu tun. :) – friedo

+0

Eval funktioniert. $ Bedingung {'Hub'} {'1'} = Sub {return $ Grad> = 5}; funktioniert nicht für mich, weil die Variablen definiert werden müssen, damit das funktioniert - meine Variablen werden später im Code definiert. Danke :) – bioinformant

2

Was erwartest du? Zeichenfolgenwerte werden als true interpretiert, wenn sie nicht leer sind.

[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 

Wenn Sie möchten, Code dynamisch bewerten in Ihren Bedingungen, haben Sie eval zu untersuchen. Beispiel:

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); 
} 

druckt

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

'eval' ist der falsche Weg, dies zu tun. – friedo

+0

Ja, Code in Strings zu speichern ist böse. – themel

+1

Während eine Warnung über 'eval' gerechtfertigt ist, ist es nicht immer" der falsche Weg ". Manchmal ist der schnellste und einfachste Weg in Ordnung. Nicht jedes Perl-Skript behandelt die Lebenserhaltung auf der Raumstation oder führt das Skalpell eines Roboter-Chirurgen. – dan1111

4

Sie versuchen, '$ Grad> = 5' als echten Code zu bewerten. Anstatt zu versuchen, die Zeichenfolge als Code auszuwerten (was mit eval erledigt werden kann), ist es normalerweise sicherer und oft robuster, stattdessen eine Codereferenz zu übergeben. Sie können einen Generator-Unterprogramm verwenden, um bedingte U-Boote auf Nachfrage zu erzeugen, wie folgt aus:

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

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

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

Es ist ein wenig komplizierter wird, wenn Sie die >= (dh die Beziehung selbst) wollen auch dynamisch erstellt werden. Dann haben Sie ein paar Möglichkeiten. Man bringt Sie zurück zu Stringy Eval, mit all seinen Risiken (besonders, wenn Sie beginnen, Ihren Benutzer die Zeichenfolge angeben zu lassen). Die andere wäre eine Nachschlagetabelle in Ihrem generate_condition() Sub.

generate_condition() gibt eine Subroutinenreferenz zurück, die bei Aufruf die Bedingung auswertet, die zur Erstellungszeit gebunden wurde.

Hier ist eine verallgemeinerte Lösung, die alle Bedingungen von Perl akzeptiert und sie zusammen mit den getesteten Argumenten in eine Subroutine einfügt. Die Subref kann dann aufgerufen werden, um die Bedingung zu bewerten:

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"; 

Oft, wenn man diese Art von Dingen konstruiert ein in Verlassen einen Parameters offen interessiert ist bei Call-Zeit zu binden, anstatt bei Unterprogramm Herstellung Zeit.Angenommen, Sie möchten nur die Parameter "$bound" und "$ relation" binden, aber lassen Sie "$test" zur Zeit des Unterprogrammaufrufs offen für die Spezifikation.

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

Und dann es wie folgt aufrufen: Sie würden Ihre Unter Generation wie diese ändern

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

Wenn das Ziel die späte Bindung sowohl in der relationalen Auswertung der linken und rechten Seite zu schaffen, dies funktioniert:

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"; } 

diese Art von Werkzeug in die Kategorie der funktionalen Programmierung fällt, und befindet sich im schönen Detail in Mark Jason Dominus Buchbedeckt

+0

Ihr erster Codeblock hat Probleme: http://pastie.org/4515233 - sollte \ $ degree/$$ test verwenden. – themel

+0

Sie benutzen es einfach falsch. Es sollte nicht $ Grad betrachten, nachdem das Sub hergestellt wurde. Es ist beabsichtigt, $ degree und $ test zu dem Zeitpunkt zu sperren, zu dem die Bedingungsuntereinheit erzeugt wird. Wenn Sie $ degree aktualisieren möchten, müssen Sie entweder ein neues Sub (mit neuen Werten gebunden) generieren oder das zweite Code-Snippet verwenden, das den Testwert frei lässt, bis das anonyme Sub aufgerufen wird. – DavidO

+0

Eh, okay, aber das scheint ein wenig sinnlos - warum die Bedingung jedes Mal bewerten, wenn der Rückgabewert konstant ist? – themel

Verwandte Themen