Perl Tkx: как передать переменную в качестве параметра для обратного вызова кнопки

58
6

Учитывая этот фрагмент кода Perl/Tkx:

@itemList = ({'attrib1' => 'name1', 'attrib2' => 'value1'},
{'attrib1' => 'name2', 'attrib2' => 'value2'});
$row = 0;
foreach $item (@itemList) {
push(@btn_list, new_ttk__button(-text => $item->{'attrib1'}, -command => sub {do_something($item->{'attrib2'});}));
$btn_list[-1]->g_grid(-column => 0, -row => $row);
$row++;
}

(В реальной программе @itemList заполняется из редактируемого пользователем файла конфигурации.)

Я вижу две кнопки с надписью "name1" и "name2". Но когда я нажимаю на любую кнопку, кажется, что параметр, который передается $itemList[1]->{'attrib2'} всегда равен $itemList[1]->{'attrib2'}; т.е. 'attrib2' последнего элемента массива @itemList. Я бы хотел, чтобы первая кнопка do_something($itemList[0]->{'attrib2'} а второй вызов do_something($itemList[1]->{'attrib2'}.

Что я делаю неправильно, пожалуйста и спасибо?

спросил(а) 2015-09-29T17:46:00+03:00 5 лет назад
1
Решение
82

Вы столкнулись с тонкой особенностью for циклов в Perl. Сначала решение: используйте my в цикле for. Тогда $item сможет создать правильное закрытие в анонимном суб, объявляемом позже в цикле.

for my $item (@itemlist) {
push(@btn_list, new_ttk__button(
-text => $item->{'attrib1'},
-command => sub {do_something($item->{'attrib2'});}));
$btn_list[-1]->g_grid(-column => 0, -row => $row);
$row++;
}

Дальнейшее объяснение: Perl неявно локализует предметную переменную цикла for. Если вы не используете my в цикле for, цикл будет использовать локализованную версию переменной пакета. Это делает ваш код эквивалентным:

package main;
$main::item = undef;
@itemList = ({'attrib1' => 'name1', 'attrib2' => 'value1'},
{'attrib1' => 'name2', 'attrib2' => 'value2'});
$row = 0;
foreach (@itemList) {
local $main::item = $_;
push(@btn_list, new_ttk__button(
-text => $main::item->{'attrib1'},
-command => sub {do_something($main::item->{'attrib2'});}));
$btn_list[-1]->g_grid(-column => 0, -row => $row);
$row++;
}
# at the end of the loop, value of $main::item restored to undef

Ваши анонимные подсистемы по-прежнему относятся к переменной пакета $main::item, независимо от того, какое значение имеет значение этой переменной во время вызова этих подпрограмм, что, вероятно, является undef.

Сокращенное решение: use strict

Дополнительное доказательство концепции. Попытайтесь угадать, что выдает следующая программа:

@foo = ( { foo => 'abc', bar => 123 },
{ foo => 'def', bar => 456 } );

my @fn;
foreach $foo (@foo) {
push @fn, sub { "42" . $foo->{bar} . "\n" };
}
foreach my $foo (@foo) {
push @fn, sub { "19" . $foo->{foo} . "\n" };
}
print $_->() for @fn;

Вот ответ:

42
42
19abc
19def

ответил(а) 2015-09-29T18:42:00+03:00 5 лет назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема