3

我正在关注Perl 的 HTML::Template 模块上的教程。这是模板:

<!--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=(...)to my @rows = [...])时,代码可以正常工作;它显示一个包含数据的表格。

正如我通过阅读这篇文章所了解的,第一种形式是从列表中定义的数组,第二种形式是对匿名数组的引用。我仍然不清楚为什么第一种形式不起作用。感谢您为我澄清这一点。

4

3 回答 3

7

您正在遵循的教程包含错误。线

$template->param( language => @languages );

应该

$template->param( language => \@languages );

为什么?简短的回答:您传递给的循环名称的右侧param必须是对数组的引用,而不是数组。

长答案:当您将参数传递给函数或方法时,所有参数都会扩展为一个长列表。这是初学者的常见错误来源。因此,在您的代码(以及本教程的代码)中,您没有向param方法传递两个参数,而是传递了四个参数(一个用于字符串“语言”,三个用于@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)。

于 2017-12-07T21:10:25.000 回答
4
language => @rows 

方法

'language', $rows[0], $rows[1], $rows[2], ...

或者

language => $rows[0],
$rows[1] => $rows[2],
...

你要

language => \@rows
于 2017-12-07T20:56:47.753 回答
1

HTML::Template 中的param()方法接受成对的参数。该对中的第一个值是您要设置的模板变量的名称,第二个是您要将该变量设置为的值。

因此,您可以进行设置单个变量的调用:

$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 模板引擎。

于 2017-12-08T08:52:55.783 回答