带通滤波中零相位和最小相位_相位器在Perl 6中的工作方式

带通滤波中零相位和最小相位

这是关于将代码从Perl 5迁移到Perl 6 的系列文章中的第六 。本文着眼于Perl 5中的特殊块 ,例如BEGINEND ,以及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中预编译模块:


   
   
# Perl 6
no precompilation ;   # this code should not be pre-compiled

但是,预编译具有一些您不希望轻易忽略的优点:

  • 数据结构设置只需完成一次。 如果您具有每次加载模块时都必须设置的数据结构,则在模块预编译时可以执行一次。 如果经常加载模块,这可能会节省大量的时间和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 每次块成功取消运行

输入并离开

ENTERLEAVE移相器非常不言自明:只要输入一个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功能相对应。

保持和撤消

KEEPUNDO移相器是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移相器将自动提交事务。

KEEPUNDO移相器为您提供了穷人软件交易记忆的基础

之前和之后

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

带通滤波中零相位和最小相位

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值