尝试按如下方式理解Perl中的一段代码
my @a = qw(A B C D E F F A K O N D);
my @b = qw(S F S T F F S);
$s=sprintf "%s %s %d %d:%d:%d %d: %s" , sub { ($a[$_[6]], $b[$_[4]], $_[3], $_[2], $_[1], $_[0], $_[5]+1900) }->(localtime), $s;
此代码位于包中的方法中,该方法已使用三个参数调用,已在此代码中的上述方法中定义了数组a
和b
,并且s
是一个定义字符串。
我可以理解的是,它是一个匿名子程序,由于本地时间函数,它应该返回多个值,但我无法理解该代码所遵循的机制。如果不让我知道,我认为我已经提供了足够的信息来提出这个问题。
答案 0 :(得分:3)
语法是在一个语句中一次封装多个值的修改的一种不寻常的方式。
此代码可能在子例程结束时使用,因为它返回值列表。真正的代码可能看起来像这样。
sub foo {
my @a = qw(Sun Mon Tue Wed Thu Fri Sat);
my @b = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my $s = q{foobar};
# wday month mday hour min sec year
return sub { ( $a[ $_[6] ], $b[ $_[4] ], $_[3], $_[2], $_[1], $_[0], $_[5] + 1900 ) }
->(localtime), $s;
}
让我们来看看。我添加了return
,以便更清楚地了解发生了什么。
有一个匿名子例程sub { ... }
,它被定义并直接被称为sub { ... }->()
。这就像一个lambda函数。此表单中的箭头运算符->
用于调用代码引用。它与直接对象语法(例如$foo->frobnicate()
)的不同之处在于箭头后面没有方法名称。
my $code = sub { print "@_" };
$code->(1, 2, 3);
这将输出:
1, 2, 3
传递列表上下文中的localtime
返回值。这是当前时间的一堆价值,正如in the documentation所述。
# 0 1 2 3 4 5 6 7 8
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime(time);
没有参数,localtime
隐式使用time
作为参数。在匿名子例程内部,这些值最终在@_
中,这是子的参数列表。所以$_[0]
是当前本地日期的秒部分。
然后有@a
和@b
,这些都是非常糟糕的名字。如果它应该是简短的,我至少在工作日和月份称他们为@d
和@m
。
匿名子返回值列表。它是当前工作日,在@a
数组中查找,其中星期日为索引0
,当前月份为@b
,日,小时,分钟和第二,年份为四位数(因此为+ 1900
)。
整体函数foo
返回此列表,另一个值$s
作为其返回值列表的最后一个元素。
如果你要print
所有这些,你会得到:
print join q{ }, foo();
__END__
Mon Sep 25 16 10 33 2017 foobar
在sprintf
的背景下,这变得更加明确。它可能会为日志条目添加时间戳。
sub log {
my $s = shift;
my @a = qw(Sun Mon Tue Wed Thu Fri Sat);
my @b = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
# 0 1 2 3 4 5 6 7
$s = sprintf "%s %s %d %d:%d:%d %d: %s",
# wday month mday hour min sec year
# 0 1 2 3 4 5 6
sub { ( $a[ $_[6] ], $b[ $_[4] ], $_[3], $_[2], $_[1], $_[0], $_[5] + 1900 ) }
# 7
->(localtime), $s;
return $s;
}
print &log('stuff happened'); # we need the & because CORE::log
我用sprintf
模式中的参数位置标记了每个值。 $s
似乎是日志消息,它被带有时间戳的新字符串替换,并且本身作为最后一个参数。
但是,这是一个非常不寻常的实现,因为localtime
将在标量上下文中返回完全相同的内容。
print &log("stuff happened\n");
print ''.localtime . ": stuff happened\n";
__END__
Mon Sep 25 16:24:40 2017: stuff happened
Mon Sep 25 16:24:40 2017: stuff happened
它是 ctime(3)的返回值。我首先想到的可能是重新实现,因为在目标系统上不存在那种时间戳,但localtime
文档也说这不依赖于系统。
此标量值的格式不依赖于语言环境,而是内置于 Perl的。对于GMT而不是当地时间使用gmtime内置。也可以看看 Time :: Local模块(用于转换秒,分钟,小时和 这样回到时间返回的整数值,以及POSIX 模块的strftime和mktime函数。
使用sub
的实现很优雅,但不是非常好的Perl-ish。但是,它完全没用,因为Perl已经提供了完全相同的格式。
您可以使用此功能轻松替换我的log
功能。
sub log {
my $s = shift;
$s = localtime . " $s";
return $s;
}
但话说回来,如果@a
和@b
可能是本地化的,那么这是有道理的。你给它们作为字母,这可能不是你真正的代码,但我可以看到这被用作例如将它翻译成德语。
use utf8;
sub log {
my $s = shift;
my @a = qw(So Mo Di Mi Do Fr Sa);
my @b = qw(Jan Feb Mär Apr Mai Jun Jul Aug Sep Okt Nov Dez);
# 0 1 2 3 4 5 6 7
$s = sprintf "%s %s %d %d:%d:%d %d: %s",
# wday month mday hour min sec year
# 0 1 2 3 4 5 6
sub { ( $a[ $_[6] ], $b[ $_[4] ], $_[3], $_[2], $_[1], $_[0], $_[5] + 1900 ) }
# 7
->(localtime), $s;
return $s;
}
print &log("stuff happened\n");
现在它更有意义。