Python を直接翻訳すると、次のようになります。
my $re_is_comment_line = qr/^\s*#/;
my $re_key_values = qr/^\s*(\w+)\s*=\s*(.*)$/;
my $re_splitter = qr/\s*,\s*/;
my $is_interesting_line= sub {
my $_ = shift;
length($_) and not /$re_is_comment_line/ and /$re_key_values/;
};
sub parse {
my @lines = @_;
my @interesting_lines = grep $is_interesting_line->($_), @lines;
my @key_values = map [/$re_key_values/], @interesting_lines;
my %splitted_values = map { $_->[0], [split $re_splitter, $_->[1]] } @key_values;
return %splitted_values;
}
違いは次のとおりです。
ifilter
が呼び出されgrep
、最初の引数としてブロックの代わりに式を取ることができます。これらはラムダとほぼ同等です。現在のアイテムは$_
変数で与えられます。についても同様map
です。
- Perl は怠惰を強調せず、めったに反復子を使用しません。これが必要な場合もありますが、通常はリスト全体が一度に評価されます。
次の例では、以下が追加されます。
- 正規表現はプリコンパイルする必要はありません。Perl は正規表現の最適化に非常に優れています。
- 正規表現でキー/値を抽出する代わりに、
split
. 結果のフラグメントの数を制限するオプションの 3 番目の引数を取ります。
map
/の全体filter
を 1 つの式で記述できます。これにより効率が向上するわけではありませんが、データの流れが強調されます。map-map-grep を下から上に読んでください (実際には右から左に、APL を考えてください)。
.
sub parse {
my %splitted_values =
map { $_->[0], [split /\s*,\s*/, $_->[1]] }
map {[split /\s*=\s*/, $_, 2]}
grep{ length and !/^\s*#/ and /^\s*\w+\s*=\s*\S/ }
@_;
return \%splitted_values; # returning a reference improves efficiency
}
しかし、ここでのよりエレガントな解決策は、従来のループを使用することだと思います。
sub parse {
my %splitted_values;
LINE: for (@_) {
next LINE if !length or /^\s*#/;
s/\A\s*|\s*\z//g; # Trimming the string—omitted in previous examples
my ($key, $vals) = split /\s*=\s*/, $_, 2;
defined $vals or next LINE; # check if $vals was assigned
@{ $splitted_values{$key} } = split /\s*,\s*/, $vals; # Automatically create array in $splitted_values{$key}
}
return \%splitted_values
}
代わりにファイルハンドルを渡すことにした場合、ループは次のように置き換えられます
my $fh = shift;
LOOP: while (<$fh>) {
chomp;
...;
}
実際のイテレータを使用します。
これで関数パラメーターを追加できますが、これは柔軟性のために最適化している場合にのみ行います。最初の例で既にコード参照を使用しました。$code->(@args)
構文でそれらを呼び出すことができます。
use Carp; # Error handling for writing APIs
sub parse {
my $args = shift;
my $interesting = $args->{interesting} or croak qq("interesting" callback required);
my $kv_splitter = $args->{kv_splitter} or croak qq("kv_splitter" callback required);
my $val_transform= $args->{val_transform} || sub { $_[0] }; # identity by default
my %splitted_values;
LINE: for (@_) {
next LINE unless $interesting->($_);
s/\A\s*|\s*\z//g;
my ($key, $vals) = $kv_splitter->($_);
defined $vals or next LINE;
$splitted_values{$key} = $val_transform->($vals);
}
return \%splitted_values;
}
これは次のように呼び出すことができます
my $data = parse {
interesting => sub { length($_[0]) and not $_[0] =~ /^\s*#/ },
kv_splitter => sub { split /\s*=\s*/, $_[0], 2 },
val_transform => sub { [ split /\s*,\s*/, $_[0] ] }, # returns anonymous arrayref
}, @lines;