2

私はこのパターンをperlでよくやっていることに気づきました

sub fun {
    my $line = $_[0];
    my ( $this, $that, $the_other_thing ) = split /\t/, $line;
    return { 'this' => $this, 'that' => $that, 'the_other_thing' => $the_other_thing};
}

明らかに、与えられた変数の配列をキーが変数と同じ名前であるマップに変換する関数の出力を返すことで、このパターンを単純化できます。

sub fun {
    my $line = $_[0];
    my ( $this, $that, $the_other_thing ) = split /\t/, $line;
    return &to_hash( $this, $that, $the_other_thing );
}

要素の量が大きくなるにつれて役立ちます。どうすればいいですか?PadWalker とクロージャを組み合わせることができるように見えますが、コア言語のみを使用してこれを行う方法が欲しいです。

編集: thb はこの問題に対する巧妙な解決策を提供しましたが、多くの難しい部分 (tm) をバイパスするため、チェックしていません。コア言語の分解セマンティクスに依存して、実際の変数から反映を追い出したい場合はどうしますか?

EDIT2:PadWalkerとクロージャーを使用することをほのめかしたソリューションは次のとおりです。

use PadWalker qw( var_name );

# Given two arrays, we build a hash by treating the first set as keys and
# the second as values
sub to_hash {
    my $keys = $_[0];
    my $vals = $_[1];
    my %hash;
    @hash{@$keys} = @$vals;
    return \%hash;
}

# Given a list of variables, and a callback function, retrieves the
# symbols for the variables in the list.  It calls the function with
# the generated syms, followed by the original variables, and returns
# that output.
# Input is: Function, var1, var2, var3, etc....
sub with_syms {
    my $fun = shift @_;
    my @syms = map substr( var_name(1, \$_), 1 ), @_;
    $fun->(\@syms, \@_);
}

sub fun {
    my $line = $_[0];
    my ( $this, $that, $other) = split /\t/, $line;
    return &with_syms(\&to_hash, $this, $that, $other);
}
4

4 に答える 4

4

PadWalkerを使用して変数の名前を取得することもできますが、それは実際には行うべきことではありません。それは壊れやすいおよび/または制限的です。

代わりに、ハッシュスライスを使用できます。

sub fun {
   my ($line) = @_;
   my %hash;
   @hash{qw( this that the_other_thing )} = split /\t/, $line;
   return \%hash;
}

必要に応じて、関数内のスライスを非表示にすることができto_hashます。

sub to_hash {
   my $var_names = shift;
   return { map { $_ => shift } @$var_names };
}

sub fun_long {
   my ($line) = @_;
   my @fields = split /\t/, $line;
   return to_hash [qw( this that the_other_thing )] @fields;
}

sub fun_short {
   my ($line) = @_;
   return to_hash [qw( this that the_other_thing )], split /\t/, $line;
}

しかし、あなたが主張するなら、ここにPadWalkerバージョンがあります:

use Carp      qw( croak );
use PadWalker qw( var_name );

sub to_hash {
   my %hash;
   for (0..$#_) {
      my $var_name = var_name(1, \$_[$_])
         or croak("Can't determine name of \$_[$_]");
      $hash{ substr($var_name, 1) } = $_[$_];
   }
   return \%hash;
}

sub fun {
   my ($line) = @_;
   my ($this, $that, $the_other_thing) = split /\t/, $line;
   return to_hash($this, $that, $the_other_thing);
}
于 2012-03-28T21:25:37.937 に答える
2

これはそれを行います:

my @part_label = qw( part1 part2 part3 );

sub fun {
    my $line = $_[0];
    my @part = split /\t/, $line;
    my $no_part = $#part_label <= $#part ? $#part_label : $#part;
    return map { $part_label[$_] => $part[$_] } (0 .. $no_part);
}

もちろん、コードのどこかでパーツに名前を付ける必要があります。上記のコードはqw()によって実行されますが、必要に応じてコードに名前を自動生成させることができます。

[*part_labels* の非常に大きなリストが予想される場合は、おそらく *(0 .. $no_part)* イディオムを避ける必要がありますが、適度なサイズのリストの場合はうまく機能します。]

以下の OP のコメントに応じて更新します 。興味深い課題を提示します。それはいいですね。以下は、あなたが望むものにどの程度近づいていますか?

sub to_hash ($$) {
    my @var_name = @{shift()};
    my @value    = @{shift()};
    $#var_name == $#value or die "$0: wrong number of elements in to_hash()\n";
    return map { $var_name[$_] => $value[$_] } (0 .. $#var_name);
}

sub fun {
    my $line = $_[0];
    return to_hash [qw( this that the_other_thing )], [split /\t/, $line];
}
于 2012-03-28T19:09:49.900 に答える
2

私があなたを正しく理解していれば、データレコードから分割された値に特定のキーシーケンスを割り当てることでハッシュを構築したいと考えています。

このコードはうまくいくようです。私があなたを誤解している場合は説明してください。

use strict;
use warnings;

use Data::Dumper;
$Data::Dumper::Terse++;

my $line = "1111 2222 3333 4444 5555 6666 7777 8888 9999\n";

print Dumper to_hash($line, qw/ class division grade group kind level rank section tier  /);

sub to_hash {
  my @fields = split ' ', shift;
  my %fields = map {$_ => shift @fields} @_;
  return \%fields;
}

出力

{
  'division' => '2222',
  'grade' => '3333',
  'section' => '8888',
  'tier' => '9999',
  'group' => '4444',
  'kind' => '5555',
  'level' => '6666',
  'class' => '1111',
  'rank' => '7777'
}

任意の 2 つのリストからハッシュを作成するより一般的なソリューションについては、次のzip_by関数をお勧めします。List::UtilsBy

use strict;
use warnings;

use List::UtilsBy qw/zip_by/;
use Data::Dumper;
$Data::Dumper::Terse++;

my $line = "1111 2222 3333 4444 5555 6666 7777 8888 9999\n";

my %fields = zip_by { $_[0] => $_[1] }
    [qw/ class division grade group kind level rank section tier  /],
    [split ' ', $line];

print Dumper \%fields;

出力は、最初のソリューションの出力と同じです。

配列参照のリストの代わりに配列のペアを取るpairwise関数も参照してください。List::MoreUtils

于 2012-03-28T20:43:10.900 に答える
1

自分で Perl コードを解析する以外to_hashに、コア言語だけを使用して関数を実行することはできません。呼び出される関数は、それらの引数が変数であるか、他の関数からの戻り値であるか、文字列リテラルであるか、または何を持っているかを認識していません。そして、それは気にしませんし、気にするべきではありません。

于 2012-03-28T19:00:35.097 に答える