带通滤波中零相位和最小相位
这是关于将代码从Perl 5迁移到Perl 6 的系列文章中的第六篇 。本文着眼于Perl 5中的特殊块 ,例如BEGIN和END ,以及Perl中所谓的相位器在语义上的细微变化。 6。
Perl 6已将一些Perl 5功能概括为相位器,而Perl 5的特殊功能块并未涵盖这些相位器。它还添加了其他相位器,而这些相位器根本没有任何(标准)Perl 5功能覆盖。
不属于程序正常执行流程的一部分。 运行时执行程序根据相位器的类型和上下文来决定何时运行相位器。 因此,Perl 6中的所有相位器均以大写字母拼写,以使其脱颖而出。概述
让我们从Perl 5的特殊块及其在Perl 6中执行的顺序开始,对它们进行概述:
Perl 5 | Perl 6 | 笔记 |
---|---|---|
BEGIN | 开始 | 加载预编译的代码时无法运行 |
UNITCHECK | 检查 | |
CHECK | Perl 6中没有等效项 | |
INIT | 在里面 | |
END | 结束 |
Perl 6中的这些相位器通常被称为程序执行相位器,因为它们与完整程序的执行相关,无论它们在程序中的位置如何。
开始
Perl 5中的BEGIN特殊块和Perl 6中的BEGIN移相器的语义是相同的。 它指定了要在解析后立即执行的一段代码(因此, 在整个程序(也就是编译单元)被解析之前)。
但是,在Perl 6中使用BEGIN有一个警告:默认情况下,Perl 6中的模块是预编译的,而与Perl 5则没有任何模块或脚本的预编译相反。
作为Perl 6模块的用户或开发人员,您不必考虑模块是否应该进行预编译(再次)。 在安装模块时以及每次Rakudo Perl 6更新之后,所有这些操作都会在后台自动完成。 每当开发人员对模块进行更改时,它也会自动完成。 您可能会注意到的唯一一件事是加载模块时的延迟很小。
这意味着仅在发生预编译时才执行BEGIN块, 而不是在每次加载模块时才执行。 这与Perl 5不同,在Perl 5中,模块通常仅作为加载模块时编译的源代码存在(即使该模块可以加载已编译的本机库组件)。
当将代码从Perl 5移植到Perl 6时,这可能会引起一些不愉快的意外,因为预编译可能是很久以前甚至在另一台机器上进行的(如果是从OS分发的软件包中安装的)。 考虑使用环境变量的值来启用调试的情况。 在Perl 5中,您可以这样写:
# Perl 5
my
$DEBUG
;
BEGIN
{
$DEBUG
=
$ENV
{ DEBUG
}
//
0
}
这在Perl 5中会很好用,因为每次加载模块都会对其进行编译,因此BEGIN块会在每次加载模块时运行。 $ DEBUG的值将是正确的,具体取决于环境变量的设置。 但是在Perl 6中却不是这样。由于BEGIN移相器仅执行一次,因此在预编译时, $ DEBUG变量将具有在模块预编译时而不是在模块加载时确定的值!
一个简单的解决方法是禁止在Perl 6中预编译模块:
但是,预编译具有一些您不希望轻易忽略的优点:
数据结构设置只需完成一次。 如果您具有每次加载模块时都必须设置的数据结构,则在模块预编译时可以执行一次。 如果经常加载模块,这可能会节省大量的时间和CPU。
它可以加载模块快得多 。 因为不需要解析任何源代码,所以预编译模块的加载速度比一遍又一遍地编译的模块快得多。 一个主要的例子是Perl 6的核心设置,这是用Perl 6编写的部分。它由一个64 KLOC / 2MB源文件(从许多单独的源文件中生成,用于维护)组成。 在Perl 6安装期间,大约需要一分钟的时间来编译此源文件。 在Perl 6启动时,加载此预编译的代码大约需要125毫秒。 这几乎是500倍的速度提升!
隐式使用BEGIN功能的Perl 5 和 Perl 6的其他一些功能也有相同的警告。 以下面的示例为例,我们希望常量DEBUG具有环境变量DEBUG的值,或者如果该变量不可用,则其值为0 :
# Perl 5
use constant DEBUG
=>
$ENV
{ DEBUG
}
//
0
;
# Perl 6
my constant DEBUG
=
%
*ENV
<DEBUG>
//
0
;
Perl 6中最好的等效项可能是INIT移相器:
# Perl 6
INIT
my
\DEBUG
=
%
*ENV
<DEBUG>
//
0
;
# sigilless variable bound to value
与Perl 5一样, INIT移相器在执行开始之前运行。 您还可以将Perl 6的模块预编译行为用作功能:
# Perl 6
say
"This module was compiled at { BEGIN DateTime.now }"
;
# This module was compiled at 2018-10-04T22:18:39.598087+02:00
但是稍后会详细介绍该语法。
统一
Perl 5中的UNITCHECK特殊块的功能是由Perl 6中的CHECK移相器执行的。 它指定当在当前编译单元的编译完成将要执行的一段代码。
检查
Perl 5 CHECK特殊块的Perl 6中没有 等效项 。 主要原因是您可能不应该再使用Perl 5中的CHECK特殊块了。 请改用UNITCHECK,因为它的语义要好得多。 (自5.10版开始提供。)
在里面
Perl 6中的INIT移相器的功能与Perl 5中的INIT特殊块相同。它指定了要在编译单元中的代码执行之前执行的一段代码。
在Perl 6的预编译模块中, INIT移相器可以替代BEGIN移相器。
结束
该END相位器的在Perl 6的功能是一样的END特殊块的在Perl 5.它指定毕竟在编译单元中的代码已被执行时,或者当代码决定退出(无论是有意或将要执行的代码段意外,因为会引发异常)。
一个例子
这是一个使用所有四个程序执行移相器及其Perl 5特殊块对应物的示例
# Perl 5
say
"running in Perl 5"
;
END
{
say
"END"
}
INIT
{
say
"INIT"
}
UNITCHECK
{
say
"CHECK"
}
BEGIN
{
say
"BEGIN"
}
# BEGIN
# CHECK
# INIT
# running in Perl 5
# END
# Perl 6
say
"running in Perl 6"
;
END
{
say
"END"
}
INIT
{
say
"INIT"
}
CHECK
{
say
"CHECK"
}
BEGIN
{
say
"BEGIN"
}
# BEGIN
# CHECK
# INIT
# running in Perl 6
# END
不仅仅是特殊的块
Perl 6中的相位器具有其他功能,使其不仅限于特殊块。
不需要块
Perl 6中的大多数移相器不必是一个Block (即,花括号之间的代码)。 它们也可以由不带花括号的单个语句组成。 这意味着如果您在Perl 5中编写了此代码:
# Perl 5
# need to define lexical outside of BEGIN scope
my
$foo
;
# otherwise it won't be known in the rest of the code
BEGIN
{
$foo
=
%
*ENV
<FOO>
//
42
}
;
您可以在Perl 6中将其编写为:
# Perl 6
# share scope with surrounding code
BEGIN
my
$foo
=
%
*ENV
<FOO>
//
42
;
可能会返回一个值
所有程序执行移相器都返回其代码的最后一个值,以便您可以在表达式中使用它们。 上面使用BEGIN的示例也可以写成:
# Perl 6
my
$foo
=
BEGIN
%
*ENV
<FOO>
//
42
;
与BEGIN移相器一起使用时,您将创建一个无名常量并在运行时分配它。
由于模块的预编译,如果要在模块中进行这种类型的初始化,则最好使用INIT移相器:
# Perl 6
my
$foo
= INIT
%
*ENV
<FOO>
//
42
;
这样可以确保在加载模块时(而不是在预编译时)确定该值(通常在模块安装期间发生一次)。
其他相位器在Perl 6
如果只对学习Perl 5特殊块在Perl 6中的工作方式感兴趣,则可以跳过本文的其余部分。 但是您会错过人们已经实现的许多不错且有用的功能。
块环相位器
块和环路移相器始终与周围的块相关联,无论它们在块中的位置如何。 除了不限于只使用其中之一,尽管您可能会说不止一个不能改善可维护性。
请注意,就这些移相器而言,任何子程序或方法 也都被视为“块”。
名称 | 描述 |
---|---|
ENTER | 每次进入块时运行 |
LEAVE | 每次离开块时运行 |
PRE | 运行块之前检查条件 |
POST | 运行块后检查返回值 |
KEEP | 每次成功离开块时运行 |
UNDO | 每次块成功取消运行 |
输入并离开
ENTER和LEAVE移相器非常不言自明:只要输入一个Block,就会调用ENTER移相器。 每当离开一个块(正常地或通过异常)时,都会调用LEAVE移相器。 一个简单的例子:
# Perl 6
say
"outside"
;
{
LEAVE say
"left"
;
ENTER say
"entered"
;
say
"inside"
;
}
say
"outside again"
;
# outside
# entered
# inside
# left
# outside again
返回ENTER相位器的最后一个值,以便可以在表达式中使用它。 这是一个人为的例子:
# Perl 6
{
LEAVE say
"stayed "
~
( now
- ENTER now
)
~
" seconds"
;
sleep
2
;
}
# stayed 2.001867 seconds
LEAVE相位器与许多其他现代编程语言中的DEFER功能相对应。
保持和撤消
KEEP和UNDO移相器是LEAVE移相器的特殊情况。 根据周围块的返回值调用它们。 如果在返回值上调用定义的方法的结果为True ,则将调用任何KEEP移相器。 如果定义的调用结果不是True ,则将调用任何UNDO移相器。 该块的实际值将在主题中可用(即$ _ )。
一个人为的例子可以阐明:
# Perl 6
for
42
, Nil
{
KEEP
{ say
"Keeping because of $_"
}
UNDO
{ say
"Undoing because of $_.perl()"
}
$_
;
}
# Keeping because of 42
# Undoing because of Nil
就像现实生活中的例子一样:
# Perl 6
{
KEEP
$dbh
. commit
;
UNDO
$dbh
. rollback
;
...
# set up a big transaction in a database
True
;
# indicate success
}
因此,如果在数据库中设置大事务时出现任何问题, UNDO移相器将确保可以回滚该事务。 相反,如果成功离开该块,则KEEP移相器将自动提交事务。
KEEP和UNDO移相器为您提供了穷人软件交易记忆的基础 。
之前和之后
PRE移相器是ENTER移相器的特殊版本。 POST相位器是LEAVE相位器的特例。
如果可以进入模块,则PRE移相器应返回一个真值。 如果没有,则将引发异常。 POST相位器接收该Block的返回值,并且如果可以离开该Block而不会引发异常,则可以返回一个true值。
一些例子:
# Perl 6
{
PRE
{ say
"called PRE"
; False
}
# throws exception
...
}
say
"we made it!"
;
# never makes it here
# called PRE
# Precondition '{ say "called PRE"; False }' failed
# Perl 6
{
PRE
{ say
"called PRE"
; True
}
# does NOT throw exception
POST
{ say
"called POST"
; False
}
# throws exception
say
"inside the block"
;
# also returns True
}
say
"we made it!"
;
# never makes it here
# called PRE
# inside the block
# called POST
# Postcondition '{ say "called POST"; False }' failed
如果只想检查某个块是否返回特定的值或类型,则最好为该块指定一个返回签名。 注意:
# Perl 6
{
POST
{
$_
~~ Int
}
# check if the return value is an Int
...
# calculate result
$result
;
}
只是一种非常round回的说法:
# Perl 6
-->
Int
{
# return value should be an Int
...
# calculate result
$result
;
}
通常,仅在必要的检查非常涉及并且无法简化为简单的类型检查的情况下,才使用POST相位器。
环路移相器
循环移相器是特定于循环结构的特殊类型的块移相器。 一个在第一次迭代( FIRST )之前运行,一个在每次迭代( NEXT )之后运行,一个在最后一次迭代( LAST )之后运行。
名称 | 描述 |
---|---|
FIRST | 在第一次迭代之前运行 |
NEXT | 在每个完成的迭代之后或与下一个迭代一起运行 |
LAST | 在最后一次迭代之后或最后一次运行 |
名称不言自明。 一个人为的例子:
# Perl 6
my
$total
=
0
;
for
1
..
5
{
$total
+=
$_
;
LAST say
"------ + \n $total.fmt('%6d')"
;
FIRST say
"values \n ======"
;
NEXT say
. fmt
(
'%6d'
)
;
}
# values
# ======
# 1
# 2
# 3
# 4
# 5
# ------ +
# 15
循环结构包括循环 ; 直到 重复/同时 重复和直到 为 ; 以及地图 , 深度图 , 平面图 。
如果需要,可以将Loop相位器与其他Block相位器一起使用,但这通常是不必要的。
摘要
除了在Perl 6中具有与之相对应的Perl 5特殊块(称为相位器)之外,Perl 6还具有许多与代码和循环结构块相关的特殊用途的相位器。 Perl 6还具有与异常处理和警告,事件驱动的编程以及文档(pod)解析有关的阶段器。 这些将在本系列的后续文章中介绍。
翻译自: https://opensource.com/article/18/10/how-phasers-work-perl-6
带通滤波中零相位和最小相位