Perl: скобки против скобок для определения массива, почему считается скаляр?
Я следовал этому руководству по модулю HTML::Template для Perl. Вот шаблон:
<!--template2.tmpl-->
<html>
<body>
<table>
<tr>
<th>Language</th>
<th>Description</th>
</tr>
<tmpl_loop name="language">
<tr>
<td><tmpl_var name="language_name"></td>
<td><tmpl_var name="description"></td>
</tr>
</tmpl_loop>
</table>
</body>
</html>
И вот тестовая программа CGI:
#!"C:\Strawberry\perl\bin\perl.exe" -wT
use CGI qw(:all);
use CGI::Carp qw(fatalsToBrowser);
use HTML::Template;
my @rows = (
{
language_name => 'C#',
description => 'Created by Microsoft'
},
{
language_name => 'PHP',
description => 'Hypertext Preprocessor'
},
{
language_name => 'Haskell',
description => 'Functional language'
},
);
print header;
my $template=HTML::Template->new(filename=>'template2.tmpl');
$template->param(language => @rows);
print $template->output();
Это терпит неудачу со следующей ошибкой: HTML::Template::param() : attempt to set parameter 'language' with a scalar - parameter is not a TMPL_VAR!
Однако, когда я меняю определение @rows с круглых скобок (с my @rows=(...)
в my @rows = [...]
) код работает нормально; он отображает таблицу с данными.
Как я понял из прочтения этой статьи, первая форма - это массив, определенный из списка, а вторая - ссылка на анонимный массив. Мне все еще не ясно, почему первая форма не работает. Буду признателен за разъяснение этого для меня.
3 ответа
Учебник, который вы читаете, содержит ошибку. Линия
$template->param( language => @languages );
должно быть
$template->param( language => \@languages );
Зачем? Краткий ответ: правая часть имени цикла, которое вы передаете param
должен быть ссылкой на массив, а не массив.
Длинный ответ: когда вы передаете аргументы функции или методу, все аргументы раскрываются в один длинный список. Это распространенный источник ошибок для начинающих. Таким образом, в вашем коде (и в коде учебника) вы не передаете два параметра param
метод, вы передаете четыре (один для строки 'language' и три для элементов @languages.
Вот пример распутывания этого списка аргументов. Если у вас есть три переменные следующим образом:
my $scalar = 'bear';
my @array = ('rat', 'moose', 'owl');
my %hash = (mass => 500, units => 'kg');
и вы передаете их функции следующим образом:
some_function($scalar, @array, %hash);
тогда функция увидит восемь аргументов: 'bear'
, 'rat'
, 'moose'
, 'owl'
, 'mass'
, 500
, 'units'
, а также 'kg'
! Возможно, еще более удивительно, что два набора значений из хэша могут быть переданы в другом порядке, поскольку хэши не сохраняются и не извлекаются в определенном порядке.
Ваше решение изменить скобки на квадратные скобки работает, но не по очень веской причине. Списки в скобках (которые могут храниться в массивах или хэшах); квадратные скобки разграничивают ссылки на массивы. Таким образом, ваш квадратный код создает ссылку на анонимный массив, который затем сохраняется как первый (и единственный) элемент вашего именованного массива. @rows
, Вместо этого вы должны либо сохранить ссылку на массив (разделенную квадратными скобками) в скалярной переменной (скажем, $rows
), или вы должны использовать скобки, сохранить список в массиве @rows
и передать ссылку на этот массив param
метод (используя обратную косую черту, как я делал выше с \@languages
).
language => @rows
средства
'language', $rows[0], $rows[1], $rows[2], ...
или же
language => $rows[0],
$rows[1] => $rows[2],
...
Ты хочешь
language => \@rows
param()
Метод в HTML::Template принимает пары аргументов. Первое значение в паре - это имя переменной шаблона, которую вы хотите установить, а второе - это значение, в которое вы хотите установить эту переменную.
Таким образом, вы можете сделать вызов, который устанавливает одну переменную:
$template->param(foo => 1);
Или вы можете установить несколько переменных за один вызов:
$template->param(foo => 1, bar => 2, baz => 3);
По понятным причинам имена переменных, указанные в вашем вызове param()
все должны быть переменными, которые определены в вашем шаблоне (либо как стандарт tmpl_var
переменные или как цикл tmpl_loop
переменные).
Если вы устанавливаете tmpl_loop
переменная (как в данном случае), то ассоциированное значение должно быть ссылкой на массив, содержащий ваши значения. Существует некоторая попытка объяснить это в документации дляparam()
, но я могу увидеть, как это может быть неясно, поскольку он просто делает это, показывая примеры в квадратных скобках (конструктор ссылки на массив), а не фактически объясняя требование.
Причина этого заключается в том, что список параметров, передаваемых подпрограмме в Perl, "сплющен" - поэтому массив разбит на отдельные элементы. Это означает, что при прохождении:
$template->param(languages => @rows);
Perl видит это как:
$template->param(languages => $row[0], $row[1] => $row[2]);
Элементы вашего массива являются хеш-ссылками. Это означает, что $row[1]
будет интерпретироваться как строковая ссылка на хеш (что-то вроде "HASH(0x12345678)"), которая определенно не является именем одной из переменных в вашем шаблоне.
Так как же это исправить? Ну, есть несколько альтернатив. Вы наткнулись на плохого. Вы использовали такой код:
@rows = [ ... ];
Это создает @rows
массив с единственным элементом, который является ссылкой на ваш реальный массив. Это означает, что:
$template->param(language => @rows);
интерпретируется как:
$template->param(language => $rows[0]);
И в качестве $rows[0]
это ссылка на ваш массив, все это работает.
Гораздо лучше было бы явно передать ссылку на @rows
,
@rows = ( ... ); # your original version
$template->param(language => \@rows);
Или создать ссылку на массив, хранящийся в скаляре.
$rows = [ ... ];
$template->param(language => $rows);
Там действительно нечего выбирать между этими двумя вариантами.
Тем не менее, я бы попросил вас подумать, почему вы тратите время на изучение HTML::Template. Прошло много лет с тех пор, как я видел, как это используется. Template Toolkit, похоже, стал фактически стандартным движком шаблонов Perl.