perl调用其他的perl_如何使Perl更优雅

perl调用其他的perl

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

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

Moose在Perl 5中启发了许多其他现代对象系统,尤其是MooMouse 。 在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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值