2012-03-28 1 views
2

Я считаю себя делает этот паттерн много в PerlКак преобразовать массив в хэш, с именами переменных, отображаемыми как ключи в 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 &, но мне хотелось бы сделать это, используя только основной язык.

EDIT: 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); 
} 
+0

И ваш вопрос ...? –

+0

Упс, извините. Ты поймал меня в середине редактирования. :) – user787747

ответ

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

@ user787747, Исправлена ​​ошибка и добавлена ​​версия, которая делает именно то, что вы просите, так как это плохо сказано. – ikegami

+0

Я начинаю думать, что основной язык не предоставляет инструменты, необходимые для этого, или они вышли бы к настоящему времени. Если я решит использовать Padwalker, почему это будет считаться недобросовестным в этом очень ограниченном контексте? – user787747

+0

... ну, кроме авторов собственного предупреждения. – user787747

1

Помимо разбора перловый код самостоятельно, to_hash функция не представляется возможным, используя только ядро ​​языка. Вызываемая функция не знает, являются ли эти args переменными, возвращают значения из других функций, строковые литералы или что у вас ... гораздо меньше, чем их имена. И это не так, и не должен, уход.

+0

Согласен, хотя это спорно, следует ли или нет, что информация будет подвергаться воздействию программатора через отражение. Макросы Lisp используют это все время, чтобы обеспечить новые функции определения. Я подозреваю, что большинство языков OO предоставляют это пользователю, а также через lib. – user787747

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) * идиома, но для списков среднего размера он прекрасно работает.]

Обновление в ответ на комментарий ОП ниже: Вы представляете интересную задачу. Мне это нравится. Насколько близки к тому, что вы хотите?

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]; 
} 
+0

Ницца! Но это злоупотребляет идеей, что каждое имя переменной name_N. Я не могу этого допустить. Я отредактирую вопрос, чтобы сделать это более ясным. – user787747

+0

Достаточно честный. Позвольте мне немного поработать над этим ... – thb

+0

Подождите ... поговорите слишком рано. Это работает. Почему ты позволил мне сойти с рук? – user787747

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' 
} 

Для более общего решения, которое будет строить хэш из любых двух списков, я предлагаю zip_by function from 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, которая берет пару массивов вместо списка ссылок на массивы.

+0

Ну, много раз эта часть шаблона: my ($ this, $ that, $ the_other_thing) = split/\ t /, $ line; является взаимозаменяемым с выражением соответствия. (http://prog21.dadgum.com/131.html) – user787747

+0

@ user787747 Я добавил к своему ответу, чтобы отвлечь решение далее. – Borodin

+0

Имейте upvote! – user787747