perl调用其他的perl

这是关于将代码从Perl 5迁移到Perl 6 的系列文章中的第七篇 。本文着眼于如何在Perl 6中创建类(对象)以及它与Perl 5的区别。

Perl 5具有非常基本的面向对象形式,您可能会认为这是事后才想到的。 为了改善这种情况,已经进行了几次尝试,其中最著名的是Moose ,它“很大程度上基于Perl 6对象系统,并且借鉴了CLOS,Smalltalk和许多其他语言的最佳创意”。 而且,Perl 6对象创建逻辑又从Moose中吸取了一些教训。

Moose在Perl 5中启发了许多其他现代对象系统,尤其是Moo和Mouse 。 在Perl 5中开始新项目之前,建议您阅读Modern Perl 。 其中,它描述了如何使用Moose创建类/对象。

为简单起见,本文将介绍基本Perl 5和基本Perl 6对象创建之间的一般差异。

如何提出“要点”

一幅图片价值超过一千个单词。 因此,让我们首先定义一个具有两个不可变属性xyPoint类,以及一个带有命名参数的构造函数。 这是在Perl 5中的外观:

# Perl 5
package Point {
sub new {
my $class = shift ;
my %args  = @_ ;  # maps remaining args as key / value into hash
bless \%args , $class
}
sub x { shift -> { x } }
sub y { shift -> { y } }
}

在Perl 6中:

# Perl 6
class Point {
has $ . x ;
has $ . y ;
}

如您所见,Perl 6语法更具声明性。 无需编写代码就可以拥有方法,也不需要为xy创建访问器的代码。 另请注意,您需要在Perl 6中指定而不是package

之后,在Perl 5和Perl 6中创建Point对象非常相似:

# Perl 5
my $point = Point -> new ( x => 42 , y = 666 ) ;

# Perl 6
my $point = Point . new ( x => 42 , y => 666 ) ;


唯一的区别是Perl 6使用 (句号)而不是-> (连字符+大于号)来调用方法。

错误检查

在理想情况下,将始终正确指定方法的所有参数。 不幸的是,我们并不生活在理想的世界中,因此明智的做法是在对象创建中添加错误检查。 假设您要确保xy均已指定且均为整数值。 在Perl 5中,您可以这样做:

# Perl 5
package Point {
sub new {
my ( $class , %args ) = @_ ;
die "The attribute 'x' is required" unless exists $args { x } ;
die "The attribute 'y' is required" unless exists $args { y } ;
die "Type check failed on 'x'" unless $args { x } =~ /^-?\d+\z/ ;
die "Type check failed on 'y'" unless $args { y } =~ /^-?\d+\z/ ;
bless \%args , $class
}
sub x { shift -> { x } }
sub y { shift -> { y } }
}

请原谅/ ^-?\ d + \ z /线路噪音。 这是一个正则表达式,用于检查由一个或多个十进制数字( \ d + )组成的字符串( ^ )的开头( ^ )到字符串的结尾(\ z)之前的可选( )连字符( -

这是很多额外的样板。 当然,您可以将其抽象为is_valid子例程,如下所示:

# Perl 5
sub is_valid {
my $args = shift ;
for ( @_ ) {        # loop over all keys specified
die "The attribute '$_' is required" unless exists $args -> { $_ } ;
die "Type check failed on '$_'" unless $args -> { $_ } =~ /^-?\d+\z/ ;
}
1
}

或者,您可以使用CPAN上许多参数验证模块之一,例如Params :: Validate 。 无论如何,您的代码将如下所示:

# Perl 5
package Point {
sub new {
my ( $class , %args ) = @_ ;
bless \%args , $class if is_valid ( \%args , 'x' , 'y' ) ;
}
sub x { shift -> { x } }
sub y { shift -> { y } }
}
Point -> new ( x => 42 , y => 666 ) ;     # ok
Point -> new ( x => 42 ) ;               # 'y' missing
Point -> new ( x => "foo" , y => 666 ) ;  # 'x' is not an integer

如果使用Moose,您的代码将如下所示:

# Perl 5
package Point ;
use Moose ;
has 'x' => ( is => 'ro' , isa => 'Int' , required => 1 ) ;
has 'y' => ( is => 'ro' , isa => 'Int' , required => 1 ) ;
no Moose ;
__PACKAGE__ -> meta -> make_immutable ;
Point -> new ( x => 42 , y => 666 ) ;     # ok
Point -> new ( x => 42 ) ;               # 'y' missing
Point -> new ( x => "foo" , y => 666 ) ;  # 'x' is not an integer

请注意,对于像Moose这样的对象系统,不需要像Perl 6中那样创建新的子例程。

但是,在Perl 6中,这些都是内置的。 必填属性特征表示必须指定一个属性。 如果提供的值不是可接受的类型,则指定类型(例如Int )会自动引发类型检查异常:

# Perl 6
class Point {
has Int $ . x is required ;
has Int $ . y is required ;
}
Point . new ( x => 42 , y => 666 ) ;     # ok
Point . new ( x => 42 ) ;               # 'y' missing
Point . new ( x => "foo" , y => 666 ) ;  # 'x' is not an integer

提供默认值

或者,您可能希望使属性成为可选属性,如果未指定,则将其初始化为0 。 在Perl 5中,可能看起来像这样:

# Perl 5
package Point {
sub new {
my ( $class , %args ) = @_ ;
$args { x } = 0 unless exists $args { x } ;  # initialize to 0 is not given
$args { y } = 0 unless exists $args { y } ;
bless \%args , $class if is_valid ( \%args , 'x' , 'y' ) ;
}
sub x { shift -> { x } }
sub y { shift -> { y } }
}

在Perl 6中,您将向每个属性声明添加具有默认值的赋值:

# Perl 6
class Point {
has Int $ . x = 0 ;  # initialize to 0 if not given
has Int $ . y = 0 ;
}

提供增变剂

到目前为止,在类/对象示例中,对象的属性是不可变的。 创建对象后,无法通过常规方式更改它们。

在Perl 5中,有多种方法可以创建增变器(对象上的一种方法,用于更改属性的值)。 最简单的方法是创建一个单独的子例程,该子例程将在对象中设置值:

# Perl 5
...
sub set_x {
my $object = shift ;
$object -> { x } = shift ;
}

可以缩短为:

# Perl 5
...
sub set_x { $_ [ 0 ] -> { x } = $_ [ 1 ] }  # access elements in @_ directly

因此您可以将其用作:

# Perl 5
my $point = Point -> new ( x => 42 , y => 666 ) ;
$point -> set_x ( 314 ) ;

有些人更喜欢在访问和更改属性时使用相同的子例程名称。 然后,指定参数意味着该子例程应被用作变量:

# Perl 5
...
sub x {
my $object = shift ;
@_ ? $object -> { x } = shift : $object -> { x }
}

可以缩短为:

# Perl 5
...
sub x { @_ > 1 ? $_ [ 0 ] -> { x } = $_ [ 1 ] : $_ [ 0 ] -> { x } }

因此您可以将其用作:

# Perl 5
my $point = Point -> new ( x => 42 , y => 666 ) ;
$point -> x ( 314 ) ;

这是一种经常使用的方式,但是这取决于在Perl 5中如何实现对象的实现细节。由于Perl 5中的对象通常只是具有好处的哈希引用,因此可以将对象用作哈希引用并直接访问基础哈希中的键。 但这破坏了对象的封装,并绕开了增变器可能做的任何其他检查:

# Perl 5
my $point = Point -> new ( x => 42 , y => 666 ) ;
$point -> { x } = 314 ;  # change x to 314 unconditionally: dirty but fast

创建也可以用作变异器的访问器的“官方”方式使用了左值子例程 ,但是由于各种原因,在Perl 5中并不经常使用这种子例程 。 但它非常接近的变异符在Perl 6的工作原理:

# Perl 5
...
sub x : lvalue { shift -> { x } }  # make "x" an lvalue sub

因此,您可以将其用作:

# Perl 5
my $point = Point -> new ( x => 42 , y => 666 ) ;
$point -> x = 314 ;  # just as if $point->x is a variable

在Perl 6中,也可以通过在属性声明中使用is rw特性,以声明方式来实现将访问器用作更改器,就像使用required特性一样:

# Perl 6
class Point {
has Int $ . x is rw = 0 ;  # allowed to change, default is 0
has Int $ . y is rw = 0 ;
}

这使您可以像这样在Perl 6中使用它:

# Perl 6
my $point = Point . new ( x => 42 , y => 666 ) ;
$point . x = 314 ;  # just as if $point.x is a variable

如果您不喜欢变体在Perl 6中的工作方式,则可以通过为变体添加方法来创建自己的变体。 例如,Perl 5中的set_x情况在Perl 6中可能看起来像这样:

# Perl 6
class Point {
has $ . x ;
has $ . y ;
method set_x ( $new ) { $ ! x = $new }
method set_y ( $new ) { $ ! y = $new }
}

但是, 等等$!x中的惊叹号是什么?

指示类中属性的真实名称; 它可以直接访问对象中的属性。 让我们退后一步,看看该属性的所谓的twigil (即辅助符号 )是什么意思。

'!' 特威吉尔

A $!x之类的属性声明中,表示该属性为private 。 这意味着您不能从外部访问该属性,除非该类的开发人员提供了这样做的方法。 这意味着无法通过调用.new对其进行初始化。

访问私有属性值的方法可能非常简单:

# Perl 6
class Point {
has $ ! x ;            # ! indicates a private attribute
has $ ! y ;
method x ( ) { $ ! x }  # return private attribute value
method y ( ) { $ ! y }
}

实际上,如果使用声明属性,这几乎会自动发生 twigil:

“。” 特威吉尔

$ .x之类的属性声明中,表示该属性是public 。 这意味着将为其创建访问器方法(与上面的私有属性方法示例非常相似)。 这也意味着可以通过调用.new来初始化属性。

如果您另外使用$ .x属性形式,则不是在引用属性,而是在引用其accessor 。 它是self.x的语法糖。 但是$ .x格式的优点是您可以轻松地在字符串内插值。 此外,访问器可以被子类覆盖:

# Perl 6
class Answer {
has $ . x = 42 ;
method message ( ) { "The answer is $.x" }  # use accessor in message
}
class Fake is Answer {   # subclassing is done with "is" trait
method x ( ) { 666 }   # override the accessor in Answer
}
say Answer . new . message ;  # The answer is 42
say Fake . new . message ;    # The answer is 666 (even though $!x is 42)

调整对象创建

有时,您需要对对象进行额外的检查或调整,然后才能使用它。 无需费心在Perl 6中创建对象 ,您通常可以通过提供TWEAK方法来进行所有需要的调整。 假设您还希望允许将值314视为666的替代方法:

# Perl 6
class Answer {
has Int $ . x = 42 ;
submethod TWEAK ( ) {
$ ! x = 666 if $ ! x == 314 ;  # 100 x pi is also bad
}
}

如果一个类具有TWEAK方法, 毕竟参数都已经被处理和分配给它的属性将被称为酌情(包括分配任何默认值和性状的任何处理,例如是RW是必需的 )。 在方法内部,您可以对对象中的属性执行任何操作。

注意, TWEAK方法最好实现为所谓的submethod子方法是一种特殊类型的方法,只能在类本身上执行,而不能在任何子类上执行。 换句话说,此方法具有子例程的可见性。

位置参数

最后,有时对象的接口是如此清晰,以至于您根本不需要命名参数。 相反,您要使用位置参数。 在Perl 5中,看起来像这样:

# Perl 5
package Point {
sub new {
my ( $class , $x , $y ) = @_ ;
bless { x => $x , y => $y } , $class
}
sub x { shift -> { x } }
sub y { shift -> { y } }
}

即使在Perl 6中创建对象针对使用命名参数进行了优化,也可以根据需要使用位置参数。 在这种情况下,您将必须创建自己的“ new ”方法。 顺便说一下,Perl 6中的方法没有什么特别的。您可以创建自己的方法,也可以创建一个具有其他名称的方法来充当对象构造函数:

# Perl 6
class Point {
has $ . x ;
has $ . y ;
method new ( $x , $y ) {
self . bless ( x => $x , y => $y )
}
}

这看起来与Perl 5非常相似,但是存在细微的差异。 在Perl 6中,位置参数是强制性的(除非它们被声明为可选)。 使它们具有默认值是可选的,与属性声明几乎一样,就像指示类型一样:您可以在方法的签名中指定它们:

# Perl 6
...
method new ( Int $x = 0 , Int $y = 0 ) {
self . bless ( x => $x , y => $y )
}

bless方法提供了在Perl 6中使用给定命名参数创建对象的逻辑:其接口与方法的默认实现相同。 每当您要创建类的实例化对象时,都可以调用它。

不要重复自己( DRY )这是您应始终使用的原则。 在Perl 6中使DRY更容易进行干燥的一个示例是x => $ x的语法糖(一对 ,键的名称与值的变量相同)。 在Perl 6中,这可以表示为:$ x 。 这将使上面的方法如下所示:

$ Perl 6
...
method new ( Int $x = 0 , Int $y = 0 ) { self . bless ( : $x , : $y ) }

此后,在Perl 5和Perl 6之间创建Point对象非常相似:

# Perl 5
my $point = Point -> new ( 42 , 666 ) ;

# Perl 6
my $point = Point . new ( 42 , 666 ) ;


摘要

在Perl 6中创建类主要是声明性的,而在标准Perl 5中创建对象主要是过程性的。 Perl 6中定义类的方式在语义上与Moose非常相似。 这是因为Moose受Perl 6对象创建模型的设计启发,反之亦然。

关于对象创建的性能问题一直是Perl 5和Perl 6的关注焦点。尽管Perl 6在对象创建方面提供了比Perl 5更多的功能,基准测试表明,Perl 6在创建和访问对象方面最近比Perl 5更快。

翻译自: https://opensource.com/article/18/11/how-make-perl-more-classy

perl调用其他的perl

perl调用其他的perl_如何使Perl更优雅相关推荐

  1. perl调用linux命令输出数组,当perl脚本运行时,从命令行上传递给它的参数存储在内建数组 中,它是PERL默认用来接收参数的数组...

    _ 以下关于文字排版不正确的是( ). 使用绝缘电阻表测量绝缘电阻时,应断开被测设备所有可能来电的电源,验明无电压,确认设备无人工作使用绝缘电阻表测量绝缘电阻时,应断开被测设备所有可能来电的电源,验明 ...

  2. 通过facade(尤其是realtime facade)来使代码更优雅

    本文来自pilishen.com----原文链接; 欢迎作客我们的php&Laravel学习群:109256050 该篇翻译整理自laravel创始人Taylor的文章:Expressive ...

  3. perl调用其他的perl_Perl可能是老派,但它继续吸引新用户

    perl调用其他的perl 今年早些时候,ActiveState对上一年半以来下载了我们的Perl发行版的用户进行了调查 . 我们收到了356个响应-99个商业用户和257个个人用户. 我使用Perl ...

  4. perl调用shell

    1 system perl也可以用system调用shell的命令,它和awk的system一样,返回值也是它调用的命令的退出状态.如果向system传递一个字符串作参数,则perl会调用shell来 ...

  5. perl调用shell命令并获取输出

    1 system perl也可以用system调用shell的命令,它和awk的system一样,返回值也是它调用的命令的退出状态.如果向system传递一个字符串作参数,则perl会调用shell来 ...

  6. linux perl 单例模式,Perl脚本学习经验(三)--Perl中ftp的使用

    使用use Net::FTP; Demo: my $Server = '192.168.1.1'; my $User = 'admin'; my $Password = 'admin'; my $ft ...

  7. perl mysql 数据推拉_使用Perl DBI操作MySQL的一些建议

    使用perl连接mysql,这个网上有很多案例了,一般大家都是DBI下的DBD::MySQL这个模块进行.这里做一个mask弄一个TIPS: Perl DBI MySQL的字符集为UTF8 Perl ...

  8. vim ctrlp_使用Ctrlp和Ctag使Vim更智能

    vim ctrlp by _haochuan 通过_haochuan 使用Ctrlp和Ctag使Vim更智能 (Make Your Vim Smarter Using Ctrlp and Ctags) ...

  9. 自定义linux命令工具栏,如何自定义Mac终端并使其更有用!

    原标题:如何自定义Mac终端并使其更有用! 终端应用程序是您在macOS中访问命令行的网关.它提供了带有外壳程序或命令解释器的接口,该接口可接收您的命令并调用其他命令来执行例行任务和复杂任务.如果您只 ...

最新文章

  1. loadrunner结果图分析
  2. 如何在Visual Studio中直接使用示例代码浏览器搜索下载和管理代码示例
  3. sdut 3346 sdut 3344 Runtime Error Runtime Error?
  4. (Spring)使用注解开发
  5. 联想拯救者y空间兑换代码_十代酷睿全面升级 拯救者Y7000P 2020产品解读
  6. MapXtreme 安装、编码及打包注意事项
  7. css媒体查询改变上边距,CSS媒体查询宽度或高度
  8. 腾讯云鼎实验室发布云安全攻防矩阵,绘制九大攻防路径全景图
  9. django 配置mysql_Django配置MySQL数据库方法
  10. 存储过程分页算法(收藏)
  11. [转]C++日志系统log4cxx使用总结
  12. JS连接数据库(需配置odbc)
  13. 【雷达通信】基于matlab Omiga-K算法SAR回波生成和成像【含Matlab源码 1184期】
  14. Java400道面试题通关宝典助你进大厂,赶紧收藏起来!
  15. python alpha通道_去除图像中的alpha通道或透明度
  16. autoit入门小教程_入门介绍
  17. Graph U-Nets 笔记
  18. 中转网关 (Transit Gateway) Connect连接类型集成FortiGate安全服务
  19. tensorflow中的正则化函数在_『TensorFlow』正则化添加方法整理
  20. 51/时钟周期、时钟频率、状态周期、机器周期

热门文章

  1. Docker镜像(image)详解
  2. Zookeeper和CAP的关系
  3. java来构造邻接矩阵
  4. Profile配置和加载配置文件
  5. ASP.NET MVC实践系列1-UrlRouting
  6. django-装饰器实现PV统计
  7. Django开发自己的博客系统
  8. cs-Filters
  9. Ubuntu下Android编译环境的配置
  10. UITableView分页