(五)子程序(调用&、默认参数列表@_、私有变量my与持久变量state、use strict编译指令等)

本文详细介绍了Perl语言中的子程序,包括定义、调用方式、返回值处理,以及如何使用默认参数列表@_。此外,还探讨了私有变量(my)和持久变量(state)的使用,以及use strict编译指令的作用。最后,讲解了return操作在非标量返回值中的应用。通过实例展示了如何在子程序中管理和返回不同类型的值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、子程序的定义、调用、返回

  Perl中,子程序,即用户创建的函数。子程序的定义是全局性质的,不存在所谓的私有子程序。假如定义了两个同名的子程序,那么后面的子程序会覆盖掉前边的,如果启用了警告信息,Perl会告知子程序被重复定义。

  1. 定义:子程序由关键字sub、子程序名、以及花括号封闭起来的代码块组成。
  2. 调用:子程序的调用须在子程序名前加上&(与号)来调用它,当Perl无同名内置子函数时,与号也可以省略(一般不这么做)。
  3. 返回值:子程序被调用时一定是作为表达式的某一个部分,所有的子程序都有一个返回值,通常,最后一次执行的表达式结果(不管是什么)都会被自动当成子程序的返回值
print "Hello\n";

sub marine{
  $n += 1;
  print "Hello, sailor number $n!\n";       #最后一次执行结果
}

&marine;
&marine;
$x = &marine;         #可以多次调用
print $x;             #返回1——表示最后的print语句成功执行

sub calc{
  $a = $fred + $barney;
  $b = $fred - $barney;
  $c = $fred * $barney;
  $d = $fred / $barney;
}

$fred = 30;
$barney = 3;
$result = &calc;       #函数返回值——最后一次计算值10

print "$result\n";

编译运行:

Hello
Hello, sailor number 1!
Hello, sailor number 2!
Hello, sailor number 3!
1
10

二、默认参数列表@_

  Perl子程序可以有参数,要传递参数到子程序里,只需要在调用的子程序名后边加上被括号圈引的列表表达式即可。例如:

$n = &max(10,15);           #包含两个参数的子程序调用

  默认数组变量@_Perl会自动将传入的参数列表化名为数组变量@_。参数列表需要存储在某个数组变量里,好让子程序调用,而Perl便将这个数组称之为@_。@_变量实际上是子程序的私有变量

#添加参数——默认数组:@_
sub max{                  #参数在默认数组@_里,数组里的每一个参数,即$_[0],$_[1],...
  if($_[0] > $_[1]){
    $_[0];
  }
  else{
    $_[1];
  }
}

$result = &max(10,15);
print $result . "\n";       #输出15

三、私有变量与持久变量

3.1、my私有变量

  默认情况下,Perl中的变量都是全局变量,也就是说,在程序的任何地方都可以访问他们,但是我们也可以借助my操作符来创建私有变量,也称词法变量

  • 适用范围:私有变量可以定义在任何语句块内,而不仅限于子程序语句块内。比如可在if、while、foreach语句块中使用。
  • 作 用 域: 仅限于定义它的最内层语句块(或文件)。
  • 上 下 文:my操作符并不会更改变量赋值时的上下文
  my($num1) = @_;               #有括号——列表上下文,和($num1) = @_;相同,返回参数
  my $num2 = @_;                #无括号——标量上下文,和$num2 = @_;相同,返回数值
  • 作用范围:在不加括号时,只能用来声明单个私有变量
  my $fred, $narney;            #只声明了第一个为私有变量,第二个为普通全局变量
  my ($fred, $barney);          #声明了列表,两个都为私有变量

代码示例:

#定义私有变量——关键词my
sub max{              #允许定义同名函数,但是会覆盖原有的函数(相当于删除原有函数)
  my($m, $n);         #定义私有变量列表
  ($m,$n) = @_;       #将默认参数赋值给私有变量
  if($m > $n){        #可以简写成一行:if($m > $n){ $m }else{ $n }
    $m;               #最后一行的分号可以省略    
  }
  else{
    $n;              
  }
}

$result = &max(10,15);
print $result . "\n";       #输出15

#多个数比较取最大值
sub max {
  my($max_number) = shift @_;   #取出默认数组中的第一个参数
  foreach(@_){                  #循环——依次将默认数组@_中的剩余参数付给默认变量$_
    if($_ > $max_number){
      $max_number = $_;
    }
  }
  $max_number;                  #函数最终返回值
}

$result = &max(12,18,26);       #自动忽略多余的参数
print $result . "\n";           #打印:28

$maximum = &max(3,8,15,19,2);    
print $maximum . "\n";          #打印:19

$result = &max();               #空参数赋值结果:undef

3.2、state持久变量

  在子程序中使用my操作符定义的私有变量,在每次调用子程序时,私有变量都会被重新定义。而使用state操作符定义持久变量,可使子程序在多次调用期间保留变量之前的值,作用域依然局限于子程序内部。

  说明:state声明持久变量的特性,是从Perl 5.010版本开始引入的

代码示例:

#持久性的私有变量_state    5.010之后的版本之后引入
#比较私有变量与持久变量的不同
sub marine0{
  my $k = 0;                #私有变量
     $k += 1;
  print "Hello, sailor number $k\n";
}

&marine0;
&marine0;
&marine0;

sub marine1{
  state $k = 0;             #持久变量
        $k += 1;
  print "Hello, sailor number $k\n";
}

&marine1;
&marine1;
&marine1;


#定义数组为state
sub running_sum{
  state $sum = 0;
  state @numbers;      #持久性数组
  
  #state @array = qw(a,b,c);    不支持写法
  
  foreach my $number(@_){     #默认参数数组@_
    push @numbers, $number;
    $sum += $number;
  }
  
  say "The sum of (@numbers) is $sum";      #变量与数组内插
}

&running_sum(5,6);
&running_sum(1..3);
&running_sum(4);

编译运行:

Hello, sailor number 1
Hello, sailor number 1
Hello, sailor number 1
Hello, sailor number 1
Hello, sailor number 2
Hello, sailor number 3
The sum of (5 6) is 11
The sum of (5 6 1 2 3) is 17
The sum of (5 6 1 2 3 4) is 21

四、use strict编译指令

  use strict编译指令:强制使用一些严格的、良好的编码风格。
  从Perl 5.012版本开始,如果使用编译指令指定最低的Perl版本号的话,就相当于隐式打开约束指令

use 5.012;          #自动加载strict指令

  strict编译指令含义:会要求声明事先所有新出现的变量,一般加上my操作符就可以了。也就是说,在使用变量时,尽可能的通过my进行声明。

my $bamm_bamm = 3;   #新的词法变量,区别于语句块中的私有变量

  习惯:在程序里加上use strict编译指令,对所有变量进行my预先声明。

五、return操作与返回类型(非标量)

5.1、return操作——结束子程序

  return操作立即停止执行并从子程序内返回某个值。

#关键字return_提前结束
my @names = qw/fred barney betty dino wilma betty/;
my $result = &find("betty", @names);
print "$result\n";          #打印索引:2

sub find {
  my($what, @array) = @_;
  foreach(0..$#array){      #对下标进行循环
    if($what eq $array[$_]){
      return $_;            #一发现就提前返回下标值
    }
  }
  -1;                      #不满足条件,函数返回-1
}

  我们希望这个子程序能找出@names数组中betty元素的索引,先用my声明参数名:$what为要搜索的内容,@array为供搜索的目标数组。子程序find可以位于主代码的上边,也可位于下边

5.2、非标量返回值

  子程序不仅可以返回标量值,如果在列表上下文调用它,还可以返回列表值。

#函数返回非标量——数组
my $m ;
my $n ;
$m = 10;
$n = 20;

sub get_list{
  if($m > $n){
    $n..$m;
  }
  else{
    reverse $m..$n;
  }
}

my @c = &get_list;
print "@c\n";            #数组内插,不能单独打印数组。

编译运行:

20 19 18 17 16 15 14 13 12 11 10
<think>我们正在处理一个预处理器错误,错误信息是:`error: missing binary operator before token`,具体出现在检查`EIGEN_COMP_MINGW`和`EIGEN_GNUC_STRICT_LESS_THAN`的条件中。这个错误通常发生在预处理器条件表达式中,当表达式不符合C/C++预处理器的语法要求时。 根据引用[2][3][4]中的信息,这种错误通常是由于预处理器无法识别某个宏或表达式导致的。特别是在条件编译指令(如`#if`)中,如果表达式包含未定义的宏或者使用了C++11才支持的特性(如`__has_attribute`)而编译器不支持,就会报错。 具体到我们的错误:`if EIGEN_COMP_MINGW && EIGEN_GNUC_STRICT_LESS_THAN 6 0 0`,这里的问题可能是`EIGEN_GNUC_STRICT_LESS_THAN`这个宏的用法不正确。在Eigen库中,`EIGEN_GNUC_STRICT_LESS_THAN`是一个用于检查GCC版本的宏,其定义类似于: ```c #define EIGEN_GNUC_STRICT_LESS_THAN(x,y,z) (__GNUC__ < x || (__GNUC__ == x && (__GNUC_MINOR__ < y || (__GNUC_MINOR__ == y && __GNUC_PATCHLEVEL__ < z)))) ``` 但是,在`#if`指令中,预处理器只能处理整数常量表达式,并且要求表达式是有效的。如果`EIGEN_GNUC_STRICT_LESS_THAN`被定义为上述形式,那么它不能在`#if`中使用,因为`#if`中不能使用`||`和`&&`等逻辑运算符(预处理器条件表达式有特定的语法,只能使用基本的整数运算和已定义的宏)。 实际上,在Eigen库中,通常使用`EIGEN_GNUC_AT_LEAST`和`EIGEN_GNUC_AT_MOST`等宏,并且这些宏被设计为可以在`#if`中使用。那么,为什么会出现这个错误呢? 查看Eigen的源代码(以Eigen 3.3.9为例),在`Macros.h`中,`EIGEN_GNUC_STRICT_LESS_THAN`的定义如下: ```c #define EIGEN_GNUC_STRICT_LESS_THAN(x,y,z) ((__GNUC__*10000+__GNUC_MINOR__*100+__GNUC_PATCHLEVEL__) < (x*10000+y*100+z)) ``` 这样,它就可以在`#if`中使用了,因为整个表达式是一个整数常量表达式。 因此,错误可能的原因有: 1. 编译器不是GCC,但代码尝试使用GCC的版本宏(`__GNUC__`等)。 2. 在定义`EIGEN_GNUC_STRICT_LESS_THAN`之前就使用了它,导致预处理器将其视为未定义。 3. 在`#if`中使用了错误的语法,比如`EIGEN_GNUC_STRICT_LESS_THAN`后面跟着三个参数(6,0,0)但宏定义只接受三个参数,而错误信息中却写成了`EIGEN_GNUC_STRICT_LESS_THAN 6 0 0`(没有括号和逗号)。 根据错误信息中的写法:`EIGEN_GNUC_STRICT_LESS_THAN 6 0 0`,这显然是不正确的宏调用。正确的调用应该是: ```c EIGEN_GNUC_STRICT_LESS_THAN(6,0,0) ``` 所以,问题可能出在Eigen库的某个头文件中,或者用户代码中,错误地写成了空格分隔而不是用括号和逗号。 但是,用户提到错误发生在Eigen库的编译中,所以可能是Eigen库内部的代码。我们检查Eigen的版本,因为不同版本的宏定义可能不同。 在Eigen 3.3.9中,我们可以在`Macros.h`中找到如下代码: ```c #if defined(__GNUC__) && (__GNUC__ <= 4) #define EIGEN_GNUC_AT_LEAST(x,y,z) 0 #else #define EIGEN_GNUC_AT_LEAST(x,y,z) (__GNUC__>x || (__GNUC__==x && (__GNUC_MINOR__>y || (__GNUC_MINOR__==y && __GNUC_PATCHLEVEL__>=z)))) #endif ``` 注意,这个`EIGEN_GNUC_AT_LEAST`不能在`#if`中使用,因为它使用了逻辑运算符。但是,在Eigen的后续版本(如3.4)中,已经改进了宏的定义。 实际上,在Eigen的代码库中,我们发现在`Eigen/src/Core/util/Macros.h`中,对于GCC版本检查的宏有两种:一种用于`#if`,一种用于普通条件。而`EIGEN_GNUC_STRICT_LESS_THAN`在Eigen的代码中并没有直接出现,可能是用户代码或第三方库中的宏。 因此,我们需要确定这个宏是从哪里来的。 **解决方案:** 1. **检查代码中的宏定义:** 确保在条件编译指令中使用的宏已经正确定义,并且语法正确。特别是检查`EIGEN_GNUC_STRICT_LESS_THAN`是否被定义为一个可以在`#if`中使用的整数常量表达式。 2. **更新Eigen库:** 如果使用的是较旧的Eigen版本,尝试更新到最新版本,因为新版本可能已经修复了这个问题。 3. **检查编译器兼容性:** 确保使用的编译器是GCC,并且版本符合要求。如果使用的是MinGW,确保其GCC版本足够新(至少5.0以上)。 4. **修改有问题的宏:** 如果无法更新Eigen,可以尝试修改Eigen的宏定义,将其改为可以在`#if`中使用的形式,即使用整数运算。例如,将`EIGEN_GNUC_STRICT_LESS_THAN`定义为: ```c #define EIGEN_GNUC_STRICT_LESS_THAN(x,y,z) ( (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < (x * 10000 + y * 100 + z) ) ``` 注意:这个宏在非GCC编译器上会出错,所以应该只在GCC上使用。 5. **在条件判断前确保宏已定义:** 如果宏`EIGEN_COMP_MINGW`或`EIGEN_GNUC_STRICT_LESS_THAN`未定义,预处理器会将其视为0,但可能导致条件表达式不符合语法。因此,在条件判断前,确保这些宏已经定义,或者使用`defined()`操作符。 例如,将: ```c #if EIGEN_COMP_MINGW && EIGEN_GNUC_STRICT_LESS_THAN(6,0,0) ``` 改为: ```c #if defined(EIGEN_COMP_MINGW) && EIGEN_COMP_MINGW && defined(EIGEN_GNUC_STRICT_LESS_THAN) && EIGEN_GNUC_STRICT_LESS_THAN(6,0,0) ``` 但这样写很冗长,而且`EIGEN_GNUC_STRICT_LESS_THAN`本身可能没有定义成可以独立检查的条件。 实际上,更好的做法是确保宏在未定义时不会参运算。在Eigen库中,通常会在定义这些宏之前先给一个默认值,比如: ```c #ifndef EIGEN_COMP_MINGW #define EIGEN_COMP_MINGW 0 #endif ``` 6. **参考引用[4]中的解决方案:** 该引用提到一个类似错误的解决方法是更新软件版本(将host-m4更新到1.4.19)。因此,如果错误是由于Eigen库的某个已知问题导致的,可以尝试更新Eigen库。 **总结:** 错误`missing binary operator before token`通常是由于预处理器条件表达式中存在语法错误。具体到这个问题,很可能是`EIGEN_GNUC_STRICT_LESS_THAN`宏的调用方式不正确(缺少括号和逗号)或者该宏没有被正确定义。建议检查并修正宏的调用方式,确保使用括号和逗号传递参数,即`EIGEN_GNUC_STRICT_LESS_THAN(6,0,0)`。同时,确保宏定义是有效的预处理器表达式。 如果问题仍然存在,尝试更新Eigen库到最新版本,或者根据上述建议修改宏定义。 §§相关问题§§ 1. 如何正确定义一个可以在预处理器条件中使用的宏? 2. 在C/C++中,预处理器条件表达式有哪些限制? 3. 如何检查GCC编译器的版本?</think>### 错误分析:`error: missing binary operator before token` 该错误发生在预处理器条件编译阶段,具体 Eigen 库的宏定义 `EIGEN_COMP_MINGW` 和 `EIGEN_GNUC_STRICT_LESS_THAN` 相关。核心问题是 **预处理器无法识别宏表达式中的语法结构**,常见原因如下: 1. **宏未定义或语法错误** 当 `EIGEN_COMP_MINGW` 或 `EIGEN_GNUC_STRICT_LESS_THAN` 未正确定义时,预处理器会将其视为无效 token(类似未声明的变量)。在 `#if` 指令中直接使用未定义的宏会导致语法错误。 2. **编译器兼容性问题** `EIGEN_GNUC_STRICT_LESS_THAN` 依赖 GCC 版本号(如 `6.0.0`),但预处理器要求版本比较必须转换为整数表达式。例如: ```c // 错误写法(缺少括号和运算符) #if EIGEN_COMP_MINGW && EIGEN_GNUC_STRICT_LESS_THAN 6 0 0 // 正确写法(需明确比较逻辑) #if defined(EIGEN_COMP_MINGW) && EIGEN_COMP_MINGW && \ (__GNUC__ < 6 || (__GNUC__ == 6 && __GNUC_MINOR__ < 0)) ``` 3. **头文件顺序问题** Eigen 库的宏定义可能未被正确初始化。若在包含 `<Eigen/Core>` 前定义了某些平台宏(如 `__MINGW32__`),可能导致内部宏状态不一致[^1]。 --- ### 解决方案 #### 方法 1:更新 Eigen 库 升级到最新版 Eigen(3.4+),该问题可能已被修复: ```bash git clone https://gitlab.com/libeigen/eigen.git cd eigen && git checkout tags/3.4.0 ``` #### 方法 2:手动修复宏定义 在包含 Eigen 头文件前添加以下代码: ```c // 确保 MingW 宏被正确定义 #if defined(__MINGW32__) && !defined(EIGEN_COMP_MINGW) #define EIGEN_COMP_MINGW 1 #endif // 显式定义版本比较宏 #ifndef EIGEN_GNUC_STRICT_LESS_THAN #define EIGEN_GNUC_STRICT_LESS_THAN(x,y,z) \ (__GNUC__ < x || (__GNUC__ == x && (__GNUC_MINOR__ < y || \ (__GNUC_MINOR__ == y && __GNUC_PATCHLEVEL__ < z)))) #endif // 然后包含 Eigen #include <Eigen/Core> ``` #### 方法 3:检查编译器兼容性 确认 GCC 版本符合 Eigen 要求(≥ 4.8): ```bash gcc --version # 若版本过低,需升级 ``` #### 方法 4:简化条件编译(临时方案) 若仅需绕过错误,可注释或简化报错代码段(不推荐长期使用): ```c // 原始报错代码 // #if EIGEN_COMP_MINGW && EIGEN_GNUC_STRICT_LESS_THAN(6,0,0) // 修改为(假设满足条件) #if 0 // 直接禁用条件块 ``` --- ### 根本原因 此错误本质是预处理器无法解析复杂的逻辑表达式。引用[4]指出,类似问题在动态栈大小配置(如 `SIGSTKSZ`)更新时也会触发,需确保库文件编译器规范兼容[^4]。对于 Eigen,建议优先升级库版本或显式定义缺失宏。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SD.ZHAI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值