本文共 10787 字,大约阅读时间需要 35 分钟。
带通滤波中零相位和最小相位
这是关于将代码从Perl 5迁移到Perl 6 的第六 。本文着眼中的 ,例如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中的移相器的语义是相同的。 它指定了要在解析后立即执行的一段代码(因此, 在整个程序(也就是编译单元)被解析之前)。
但是,在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中预编译模块:
# Perl 6 precompilation ; # this code should not be pre-compiled
但是,预编译具有一些您不希望轻易忽略的优点:
数据结构设置只需完成一次。 如果您具有每次加载模块时都必须设置的数据结构,则在模块预编译时可以执行一次。 如果经常加载模块,这可能会节省大量的时间和CPU。
它可以加载模块快得多 。 因为不需要解析任何源代码,所以预编译模块的加载速度比一遍又一遍地编译的模块快得多。 一个主要的例子是Perl 6的核心设置,这是用Perl 6编写的部分。它由一个64 / 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// 0 ;
Perl 6中最好的等效项可能是INIT移相器:
# Perl 6 INIT my \DEBUG = % *ENV// 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中的移相器执行的。 它指定当在当前编译单元的编译完成将要执行的一段代码。
Perl 5 CHECK特殊块的Perl 6中没有 等效项 。 主要原因是您可能不应该再使用Perl 5中的CHECK特殊块了。 请改用UNITCHECK,因为它的语义要好得多。 (自开始提供。)
Perl 6中的移相器的功能与Perl 5中的INIT特殊块相同。它指定了要在编译单元中的代码执行之前执行的一段代码。
在Perl 6的预编译模块中, INIT移相器可以替代BEGIN移相器。
该相位器的在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中的大多数移相器不必是一个 (即,花括号之间的代码)。 它们也可以由不带花括号的单个语句组成。 这意味着如果您在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// 42 } ;
您可以在Perl 6中将其编写为:
# Perl 6 # share scope with surrounding code BEGIN my $foo = % *ENV// 42 ;
所有程序执行移相器都返回其代码的最后一个值,以便您可以在表达式中使用它们。 上面使用BEGIN的示例也可以写成:
# Perl 6 my $foo = BEGIN % *ENV// 42 ;
与BEGIN移相器一起使用时,您将创建一个无名常量并在运行时分配它。
由于模块的预编译,如果要在模块中进行这种类型的初始化,则最好使用INIT移相器:
# Perl 6 my $foo = INIT % *ENV// 42 ;
这样可以确保在加载模块时(而不是在预编译时)确定该值(通常在模块安装期间发生一次)。
如果只对学习Perl 5特殊块在Perl 6中的工作方式感兴趣,则可以跳过本文的其余部分。 但是您会错过人们已经实现的许多不错且有用的功能。
块和环路移相器始终与周围的块相关联,无论它们在块中的位置如何。 除了不限于只使用其中之一,尽管您可能会说不止一个不能改善可维护性。
请注意,就这些移相器而言,任何子程序或方法 也都被视为“块”。
名称 | 描述 |
---|---|
ENTER | 每次进入块时运行 |
LEAVE | 每次离开块时运行 |
PRE | 运行块之前检查条件 |
POST | 运行块后检查返回值 |
KEEP | 每次成功离开块时运行 |
UNDO | 每次块成功取消运行 |
和移相器非常不言自明:只要输入一个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" ; 2 ; } # stayed 2.001867 seconds
LEAVE相位器与许多其他现代编程语言中的DEFER功能相对应。
和移相器是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移相器为您提供了穷人 。
移相器是ENTER移相器的特殊版本。 相位器是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 | 在最后一次迭代之后或最后一次运行 |
名称不言自明。 一个人为的例子:
# 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)解析有关的阶段器。 这些将在本系列的后续文章中介绍。
翻译自:
带通滤波中零相位和最小相位
转载地址:http://vmdzd.baihongyu.com/