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对象创建之间的一般差异。
如何提出“要点”
一幅图片价值超过一千个单词。 因此,让我们首先定义一个具有两个不可变属性x和y的Point类,以及一个带有命名参数的构造函数。 这是在Perl 5中的外观:
在Perl 6中:
如您所见,Perl 6语法更具声明性。 无需编写代码就可以拥有新方法,也不需要为x和y创建访问器的代码。 另请注意,您需要在Perl 6中指定类而不是package 。
之后,在Perl 5和Perl 6中创建Point对象非常相似:
唯一的区别是Perl 6使用。 (句号)而不是-> (连字符+大于号)来调用方法。
错误检查
在理想情况下,将始终正确指定方法的所有参数。 不幸的是,我们并不生活在理想的世界中,因此明智的做法是在对象创建中添加错误检查。 假设您要确保x和y均已指定且均为整数值。 在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子例程,如下所示:
或者,您可以使用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 )会自动引发类型检查异常:
提供默认值
或者,您可能希望使属性成为可选属性,如果未指定,则将其初始化为0 。 在Perl 5中,可能看起来像这样:
在Perl 6中,您将向每个属性声明添加具有默认值的赋值:
提供增变剂
到目前为止,在类/对象示例中,对象的属性是不可变的。 创建对象后,无法通过常规方式更改它们。
在Perl 5中,有多种方法可以创建增变器(对象上的一种方法,用于更改属性的值)。 最简单的方法是创建一个单独的子例程,该子例程将在对象中设置值:
可以缩短为:
# Perl 5
...
sub set_x
{
$_
[
0
]
->
{ x
}
=
$_
[
1
]
}
# access elements in @_ directly
因此您可以将其用作:
有些人更喜欢在访问和更改属性时使用相同的子例程名称。 然后,指定参数意味着该子例程应被用作变量:
可以缩短为:
# Perl 5
...
sub x
{
@_
>
1
?
$_
[
0
]
->
{ x
}
=
$_
[
1
]
:
$_
[
0
]
->
{ x
}
}
因此您可以将其用作:
这是一种经常使用的方式,但是这取决于在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中可能看起来像这样:
但是, 等等 : $!x中的惊叹号是什么?
! 指示类中属性的真实名称; 它可以直接访问对象中的属性。 让我们退后一步,看看该属性的所谓的twigil (即辅助符号 )是什么意思。
'!' 特威吉尔
A ! 在$!x之类的属性声明中,表示该属性为private 。 这意味着您不能从外部访问该属性,除非该类的开发人员提供了这样做的方法。 这也意味着无法通过调用.new对其进行初始化。
访问私有属性值的方法可能非常简单:
实际上,如果使用声明属性,这几乎会自动发生。 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 6中创建对象针对使用命名参数进行了优化,也可以根据需要使用位置参数。 在这种情况下,您将必须创建自己的“ new ”方法。 顺便说一下,Perl 6中的新方法没有什么特别的。您可以创建自己的方法,也可以创建一个具有其他名称的方法来充当对象构造函数:
这看起来与Perl 5非常相似,但是存在细微的差异。 在Perl 6中,位置参数是强制性的(除非它们被声明为可选)。 使它们具有默认值是可选的,与属性声明几乎一样,就像指示类型一样:您可以在新方法的签名中指定它们:
bless方法提供了在Perl 6中使用给定命名参数创建对象的逻辑:其接口与新方法的默认实现相同。 每当您要创建类的实例化对象时,都可以调用它。
不要重复自己( DRY )这是您应始终使用的原则。 在Perl 6中使DRY更容易进行干燥的一个示例是x => $ x的语法糖(一对 ,键的名称与值的变量相同)。 在Perl 6中,这可以表示为:$ x 。 这将使上面的新方法如下所示:
此后,在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