Tho*_*och 2 perl functional-programming
我必须编写Perl,尽管我对Java,Python和函数式语言更加熟悉.我想知道是否有一些惯用的方法来解析像这样的简单文件
# comment line - ignore
# ignore also empty lines
key1 = value
key2 = value1, value2, value3
Run Code Online (Sandbox Code Playgroud)
我想要一个函数,我通过文件的行传递迭代器,并返回从键到值列表的映射.但要实现功能性和结构化,我想:
什么是最现代,最惯用,最干净,功能最强的方式?代码的不同部分应该是可单独测试和可重用的.
作为参考,这是(快速入侵)我如何在Python中做到这一点:
re_is_comment_line = re.compile(r"^\s*#")
re_key_values = re.compile(r"^\s*(\w+)\s*=\s*(.*)$")
re_splitter = re.compile(r"\s*,\s*")
is_interesting_line = lambda line: not ("" == line or re_is_comment_line.match(line))
and re_key_values.match(line)
def parse(lines):
interesting_lines = ifilter(is_interesting_line, imap(strip, lines))
key_values = imap(lambda x: re_key_values.match(x).groups(), interesting_lines)
splitted_values = imap(lambda (k,v): (k, re_splitter.split(v)), key_values)
return dict(splitted_values)
Run Code Online (Sandbox Code Playgroud)
你的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;
}
Run Code Online (Sandbox Code Playgroud)
差异是:
ifilter被调用grep,并且可以将表达式而不是块作为第一个参数.这些大致相当于一个lambda.当前项目在$_变量中给出.这同样适用于map.在下一个示例中,将添加以下内容:
split.它采用可选的第三个参数来限制结果片段的数量.map/ filter东西可以用一个表达式编写.这不会提高效率,但会强调数据流.从底部向上读取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
}
Run Code Online (Sandbox Code Playgroud)
但我认为这里更优雅的解决方案是使用传统的循环:
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
}
Run Code Online (Sandbox Code Playgroud)
如果我们决定传递一个文件句柄,那么循环将替换为
my $fh = shift;
LOOP: while (<$fh>) {
chomp;
...;
}
Run Code Online (Sandbox Code Playgroud)
哪个会使用实际的迭代器.
您现在可以添加函数参数,但只有在优化灵活性而不是其他任何内容时才能执行此操作.我已在第一个示例中使用了代码引用.您可以使用$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;
}
Run Code Online (Sandbox Code Playgroud)
然后可以这样调用
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;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
310 次 |
| 最近记录: |