ファイルから配列への入力を受け取ったという要件があります。
たとえば、配列の内容が2の場合、<、3はそれぞれ$ 1、$ 2、$3です。
「2<3」の文字列があります。本当に2が3未満かどうか、条件の妥当性を確認する必要があります。
入力が文字列の場合、if()条件の入力を送信するにはどうすればよいですか?
最近Perlを紹介されましたが、もっと詳しく調べたいと思います。
ファイルから配列への入力を受け取ったという要件があります。
たとえば、配列の内容が2の場合、<、3はそれぞれ$ 1、$ 2、$3です。
「2<3」の文字列があります。本当に2が3未満かどうか、条件の妥当性を確認する必要があります。
入力が文字列の場合、if()条件の入力を送信するにはどうすればよいですか?
最近Perlを紹介されましたが、もっと詳しく調べたいと思います。
入力が単純な場合 (数値、演算子、数値)、次の方法で解決できます。
#!/usr/bin/perl
use warnings;
use strict;
my $input = shift;
my ($num1, $op, $num2) = $input =~ /([0-9]+) *([<=>]) *([0-9]+)/;
if ('=' eq $op and $num1 == $num2
or
'<' eq $op and $num1 < $num2
or
'>' eq $op and $num1 > $num2) {
print "Yes\n";
} else {
print "No\n";
}
またはそれより短く、「宇宙船」演算子を使用<=>
:
#!/usr/bin/perl
use warnings;
use strict;
my @operations = qw(= > <);
my $input = shift;
my ($num1, $op, $num2) = $input =~ /([0-9]+) *([<=>]) *([0-9]+)/;
if ($op eq $operations[$num1 <=> $num2]) {
print "Yes\n";
} else {
print "No\n";
}
式が再帰的 (つまり(2+3)>(4+7)
) である場合は、構文解析を学習する必要があります。Parse::RecDescentまたはMarpa::R2をお勧めします。
ジョナサン・レフラーが言ったように、これは危険です。そのようなコードは絶対に実行しないでください。とても危険です。
これを行う最も簡単な (そして最も危険な) 方法は、もちろんeval
. Jonathan がコメントでリンクしたドキュメントを参照してください。
より安全なオプションは、Safeを使用することです。構文の使用を、事前に定義できる Perl の非常に特定の部分に制限するコンパートメントを作成します。たとえば、Perl コードを実行できる IRC ボットや Web サイトを作成するために人々が使用するものです。その良い例は、freenode の #perl の perlbot です。
免責事項:ドキュメントをよくお読みください。このようなものをコピーしないでください。オペコードについての説明を読んでください!
コード例を次に示します。
use strict; use warnings;
use Safe;
$compartment = new Safe;
$compartment->permit(qw(:base_core));
$result = $compartment->reval(" 2 < 3 ? 1 : 0 ");
派手なステートメントを作成することもできますが、if
かなり冗長になり、エラーが発生しやすくなります。
if ('=' eq $op and $num1 == $num2
or
'<' eq $op and $num1 < $num2
or
'>' eq $op and $num1 > $num2) {
print "Yes\n";
} else {
print "No\n";
}
または(注意すれば) . を使用できますeval()
。
# check $input here
...
$input =~ s/[^=]=[^=]/==/;
if( eval $input ){ ...
ただし、使用するたびに、すべての入力をコンパイルする必要があります。
このように安全に使用することも非常に困難です。
代わりに、高度な Perl ハッカーを紹介したいと思います。
使用eval()
している間は安全に使用できます。これは、それによってコンパイルされるものを正確に制御するためです。
#!/usr/bin/perl
use warnings;
use strict;
our %compare;
our $compare_match;
BEGIN{
# list of simple ops
my @ops = qw'< <= == != > >= lt le eq gt ge ne';
for my $op (@ops){
$compare{$op} = eval"sub{ \$_[0] $op \$_[1]}";
}
push @ops, '='; # for $compare_match
$compare{'='} = $compare{'=='}; # copy sub
# longest first
@ops = sort { length($b) <=> length($a) } @ops;
local $" #"
= '|';
$compare_match = eval"qr{(?:@ops)}";
}
sub check{
my( $num1, $op, $num2 ) = @_;
if( @_ == 1 ){
($num1,$op,$num2) =
$_[0] =~ /([0-9]+) \s* ($compare_match) \s* ([0-9]+)/x;
} elsif( @_ != 3 ){
die 'wrong number of arguments'; # could be improved
}
unless( $op and exists $compare{$op} ){
die "unknown op of '$op'"; # could be improved
}
# the heart of this implementation
return $compare{$op}->($num1,$num2);
}
if( check('2<3') ){ # one arg
print "Yes\n"; # <---
}else{
print "No\n";
}
if( check('2>3') ){ # one arg
print "Yes\n";
}else{
print "No\n"; # <---
}
if( check(qw'2 < 3') ){ # three arg
print "Yes\n"; # <---
}else{
print "No\n";
}
if( check(qw'2 > 3') ){ # three arg
print "Yes\n";
}else{
print "No\n"; # <---
}
# dies
if( check(qw'2 **** 3') ){ ... }
この方法を使用すると、比較操作を簡単に追加できることに注意してください。
(私がしなければならなかったのは、それらをに追加することだけでし@ops
たBEGIN{}
)