perl调用其他的perl
这是有关将代码从Perl 5迁移到Perl 6的系列文章中的第九篇。 本文研究了Perl 5和Perl 6之间的子例程可见性的细微差异以及Perl 6的(渐进式)键入核心功能。
本文假定您熟悉的签名 -如果你没有,请阅读如何子程序签名在Perl 6的工作 ,在这一系列的第四篇文章, 然后再继续 。
子程序的可见性
在Perl 5中,默认情况下,无论在何处进行定义,命名子例程在定义它的包的范围内都是可见的:
# Perl 5
{
sub foo
{
"bar"
}
# visible outside of this scope
}
say foo
(
)
;
# bar
在Perl 6中,已命名子例程仅在定义它的词法范围内可见:
# Perl 6
{
sub foo
(
)
{
"bar"
}
# only visible in this scope
}
say foo
(
)
;
# ===SORRY!=== Error while compiling ...
# Undeclared routine:
# foo used at line ...
请注意, 对不起! 错误消息Perl 6中的内容表示在编译时找不到子例程foo 。 这是非常有用的功能,有助于防止在编写子例程的调用时在子例程名称中输入错误。
你可以考虑子程序的定义在Perl 6总是有我的面前,类似定义词法变量。 Perl 5还具有(以前是实验性的) 词汇子例程功能,必须在低于Perl 5.26的版本中专门激活该子例程 :
# Perl 5.18 or higher
no warnings
'experimental::lexical_subs'
;
use feature
'lexical_subs'
;
{
my
sub foo
{
"bar"
}
# limit visibility to this scope
}
say foo
(
)
;
# Undefined subroutine &main::foo called at ...
在Perl 5和Perl 6中都可以在子例程定义的前面加上我们的作用域指示器,但结果却微妙不同:在Perl 5中,这使子例程在作用域之外可见,但是在Perl中却不是6.在Perl 6中,子程序的查找总是词法:利用我们的子程序声明(的范围无论)的允许子程序从其所定义的命名空间外部调用:
# Perl 6
module Foo
{
our
sub bar
(
)
{
"baz"
}
# make sub visible from the outside
}
say Foo
::
bar
(
)
;
# baz
没有我们的话,这将会失败。 在Perl 5中,可以从定义它的名称空间之外调用任何子例程:
# Perl 5
package Foo
{
sub bar
{
"baz"
}
# always visible from the outside
}
say Foo
::
bar
(
)
;
# baz
在Perl 5中,旨在“私有”的子例程的名称(即仅从该范围内而不是从外部调用)通常以下划线开头。 但这不会阻止他们从外面被召唤。 在Perl 6中,不打算从外部调用的子例程完全不可见。
在我们对在Perl 6子程序定义不仅表明子程序可以从外部调用; 它还指示如果它是正在加载的模块的一部分,则将其导出。 在以后的有关模块创建和模块加载的文章中,我将介绍导出子例程。
调用子程序
当您在未启用子例程签名的情况下在Perl 5中调用子例程时,它将调用该子例程(如果存在)(在运行时确定)并将参数传递到该子例程内的@_中 。 子例程中的参数发生任何变化完全取决于子例程(请参见Perl 6中的子例程签名如何工作 )。
在Perl 6中调用子例程时,它将执行其他检查,以查看传递给该子例程的参数是否与该子例程在调用该子例程的代码之前期望的参数匹配。 Perl 6尝试尽早执行此操作-如果它确定对子例程的调用将永远不会成功,它将在编译时告诉您:
# Perl 6
sub foo
(
)
{
"bar"
}
# subroutine not taking any parameters
say foo
(
42
)
;
# try calling it with one argument
# ===SORRY!=== Error while compiling ...
# Calling foo(Int) will never work with declared signature ()
请注意,错误消息中提到了作为参数传递的值的类型( Int )。 在这种情况下,调用该子例程将失败,因为该子例程不接受传递给它的任何参数( 声明为signature() )。
其他签名功能
除了在签名中指定位置和命名参数外,您还可以指定这些参数应为哪种类型。 如果参数类型与参数类型不智能匹配,它将被拒绝。 在此示例中,子例程需要一个Str参数:
# Perl 6
sub foo
( Str
$who
)
{
"Hello $who"
}
# subroutine taking a Str parameter
say foo
(
42
)
;
# try calling it with an integer
# ===SORRY!=== Error while compiling ...
# Calling foo(Int) will never work with declared signature (Str $who)
它同时检查所需参数的数量和类型。 不幸的是,并非总是能够在编译时可靠地看到这一点。 但是将参数绑定到参数时,仍然需要在运行时进行检查:
# Perl 6
sub foo
( Str
$who
)
{
"Hello $who"
}
# subroutine taking a Str parameter
my
$answer
=
42
;
say foo
(
$answer
)
;
# try calling it with a variable
# Type check failed in binding to parameter '$who'; expected Str but got Int (42)
# in sub foo at ...
但是,如果Perl 6知道传递给子例程的变量的类型,则可以在编译时确定该调用将永远无法进行:
# Perl 6
sub foo
( Str
$who
)
{
"Hello $who"
}
# subroutine taking a Str parameter
my Int
$answer
=
42
;
say foo
(
$answer
)
;
# try calling it with an Int variable
# ===SORRY!=== Error while compiling ...
# Calling foo(Int) will never work with declared signature (Str $who)
应该清楚的是,使用键入变量和参数可以使Perl 6帮助您更快地发现问题!
逐步打字
以上通常称为渐进式打字 。 Perl 6总是在运行时执行类型检查( 动态类型 )。 但是,如果它可以在编译时确定某些东西行不通,它将告诉您。 这通常称为静态类型化 。
如果您来自Perl 5,并且具有Moose (特别是MooseX :: Types )的经验,那么您可能会担心将类型信息添加到代码中会对性能产生影响。 在Perl 6中不必担心,因为类型检查总是在Perl 6中进行,每次对变量的分配或对参数的绑定。 那是因为如果您不指定类型,Perl 6将隐式假定Any类型,该类型与(几乎)Perl 6中的所有内容都智能匹配。因此,如果您正在编写:
# Perl 6
my
$foo
=
42
您实际上已经写过:
# Perl 6
my Any
$foo
=
42
;
子例程的参数也是如此:
# Perl 6
sub foo
(
$bar
)
{
...
}
实际上是:
# Perl 6
sub foo
( Any
$bar
)
{
...
}
添加类型信息不仅有助于发现程序中的错误,还使优化器可以就可以优化的内容以及如何对其进行最佳优化做出明智的决策。
是否定义
如果您在Perl 5中指定了变量但未分配,则该变量包含未定义的值( undef ):
这在Perl 6中没有太大区别:
因此,您可以指定在变量中作为参数可接受的值类型。 但是,如果不分配这样的变量会怎样?
像在Perl 5中一样,仍未定义该变量中的值。但是,如果只想显示该变量的内容,则它不是 undef ,就像在Perl 5中那样:
# Perl 6
my Int
$foo
;
say
$foo
;
# (Int)
您将看到Perl 6中类型对象的表示形式 。与Perl 5不同,Perl 6具有许多类型化的undef 。 定义的或您自己定义的每个类都是一个类型对象。
但是,如果通常使用.new实例化一个类型对象,则该对象将按预期成为已定义的对象:
输入表情符号
如果在子例程中为参数指定约束,则还可以指示是否要使用该类型的定义值:
# Perl 6
sub foo
( Int
: D
$bar
)
{
...
}
:D与Int组合表示您需要一个Int类型的D定义值。 因为:D还是一个笑脸很大的表情符号,所以在字体上的这种装饰称为“字体笑脸”。 那么,如果将未定义的值传递给这样的子例程会发生什么呢?
# Perl 6
sub foo
( Int
: D
$bar
)
{
...
}
# only accept instances of Int
foo
( Int
)
;
# call with a type object
# Parameter '$bar' of routine 'foo' must be an object instance of
# type 'Int', not a type object of type 'Int'. Did you forget a '.new'?
细心的读者可能会意识到这会产生编译时错误。 但是可惜的是,还没有。 尽管在Perl 6中已知错误消息非常棒,但是仍然有很多工作可以使错误消息变得更好(在这种情况下更及时)。
您还可以在变量定义上使用:D类型笑脸,以确保为该变量提供初始化:
# Perl 6
my Int
: D
$foo
;
# missing initialization
# ===SORRY!=== Error while compiling ...
# Variable definition of type Int:D requires an initializer
其他类型的表情是:U(用于未定义):_(对于不关心,这是默认的):
# Perl 6
sub foo
( Int
: U
$bar
)
{
...
}
# only accept Int type object
foo
(
42
)
;
# call with an instance of Int
# Parameter '$bar' of routine 'foo' must be a type object of type 'Int',
# not an object instance of type 'Int'. Did you forget a 'multi'?
嗯...似乎被遗忘的多重选择是什么? 好吧,这是本系列的下一篇文章!
摘要
默认情况下,Perl 6中的子例程仅在定义它们的词法范围内可见。 即使加上我们的前缀也不会使它们在该词法范围之外可见,但确实允许从其范围之外的子例程调用其完整的包名称(对于package bar中的子例程bar来说是Foo :: bar() )。
Perl 6允许您使用渐进类型来确保子例程的参数或变量赋值的有效性。 这不会产生任何额外费用运行。 在代码中添加类型甚至可以使编译器在编译时捕获错误,并且可以使优化器在运行时对优化做出更好的决策。
翻译自: https://opensource.com/article/18/12/calling-subs-and-typing-perl-6
perl调用其他的perl