博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
perl调用其他的perl_如何使Perl更优雅
阅读量:2525 次
发布时间:2019-05-11

本文共 18723 字,大约阅读时间需要 62 分钟。

perl调用其他的perl

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

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

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

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

如何提出“要点”

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

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

在Perl 6中:

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

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

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

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

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

错误检查

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

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

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

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

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

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

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

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

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

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

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

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

提供默认值

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

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

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

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

提供增变剂

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

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

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

可以缩短为:

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

因此您可以将其用作:

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

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

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

可以缩短为:

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

因此您可以将其用作:

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

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

# Perl 5      
my
$point
= Point
->
new
( x
=>
42
,
=>
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
,
=>
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 $
. is rw
=
0
;
}

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

# Perl 6      
my
$point
= Point
.
new
( x
=>
42
,
=>
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 $
.
;
    method set_x
(
$new
)
{
$
! x
=
$new
}
    method set_y
(
$new
)
{
$
!
=
$new
}
}

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

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

'!' 特威吉尔

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

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

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

实际上,如果使用声明属性,这几乎会自动发生 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)

调整对象创建

有时,您需要对对象进行额外的检查或调整,然后才能使用它。 无需 ,您通常可以通过提供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      
Point
{
   
sub
new
{
       
my
(
$class
,
$x
,
$y
)
=
@_
;
       
{
x
=>
$x
,
=>
$y
}
,
$class
   
}
   
sub x
{
shift
->
{
x
}
}
   
sub
{
shift
->
{
}
}
}

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

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

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

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

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

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

$ Perl      
6
...
method
new
( Int
$x
=
0
, Int
$y
=
0
)
{
self
.
(
:
$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更快。

翻译自:

perl调用其他的perl

转载地址:http://nmdzd.baihongyu.com/

你可能感兴趣的文章
springcloud
查看>>
Binary Tree Inorder Traversal
查看>>
npm、yarn、pnpm
查看>>
洛谷 P2590 [ZJOI2008]树的统计
查看>>
软件工程结对项目博客作业
查看>>
C++ 虚函数表解析
查看>>
#define 宏定义
查看>>
【Linux学习】python脚本直接运行与nohup运行结果不同
查看>>
2017《面向对象程序设计》课程作业一
查看>>
Bootstrap基础
查看>>
Druid.jar包
查看>>
循环神经网络(Recurrent Neural Network,RNN)
查看>>
如何实现水平居中和垂直居中
查看>>
bzoj1012
查看>>
Java设计模式(五)Prototype原型模式
查看>>
Use MFC in a Static Library 和 use MFC in a Shared
查看>>
YC的基本创业建议
查看>>
发现一个nginx LUA开发Web App的框架
查看>>
数据结构——有关于栈和队列的小归纳
查看>>
Ubuntu 分区重装
查看>>