1.2 设置默认值
时间:2006-12-26 来源:xiaoshengcaicai
1.2 设置默认值
1.2.1 问题
你想给一个scalar变量提供一个默认值,而且是仅当这个变量没有值的时候提供。这种情况经常发生,你想给一个变量内嵌一个默认值,而且该值被可以从命令行的某个参数或者环境变量里的某个值所覆盖。
1.2.2 解决方案
使用操作符 || 或者 ||= , 可以作用在字符串跟数字上面。
# use $b if $b is true, else $c
$a = $b || $c;
# set $x to $y unless $x is already true
$x ||= $y;
如果 0, "0", 跟 "" 对你的变量来说是有意义的有效值,那么应该改用defined:
# use $b if $b is defined, else $c
$a = defined($b) ? $b : $c;
# the "new" defined-or operator from future perl
use v5.9;
$a = $b // $c;
1.2.3 讨论
2种方法(defined跟||)比较大的差别在于它们测试的是什么: 是已定义值呢,还是布尔真值。在Perl的世界里面有3种已定义值是布尔假值,0, "0", and ""。如果你的变量已经是这3种值中的一种,而且你想让变量保留这个值,用||是不行的。你不得不用更精细的测试方法: defined。(more elaborate three-way test)。通常情况下,如果你的程序只关心变量的布尔值而不是关心变量是否已定义的话,往往会比较方便。
在Perl里面,操作符||除了有跟其他大多数语言里面的操作符||一样的作用--限制表达式返回布尔值之外,它还有一个更有趣的属性:它返回它的第一个操作数(左边那个)。而操作符&&也同样返回最后一个计算的表达式的值,只是它这点属性很少用到。这些操作符不关心它们的操作数是字符串还是数字,引用--只要是Scalar就可以。它们只是返回第一个让整个表达式为布尔真值的操作数。这个不会影响返回值的布尔意义,但是这样的返回值会更加有用。
这个属性让你可以给变量,函数或者长一点的表达式在第一个赋值失败的情况下给它提供一个默认值。这里是一个||的例子,会把$foo的值设置成$bar的内容,如果$bar是布尔假值,那么给$foo赋值"DEFAULT VALUE"。
$foo = $bar || "DEFAULT VALUE";
这里是另外一个例子,会把$dir设置成程序第一个参数的值,如果程序没有参数,那么给它赋值"/tmp"。
$dir = shift(@ARGV) || "/tmp";
用下面这个方法可以不用改变@ARGV
$dir = $ARGV[0] || "/tmp";
如果0对$ARGV[0]来说是有意义的值,那么我们不能使用||,因为它会得到一个布尔假值就算这个值是我们想接受的。我们得采取Perl得唯一一个三元操作符?:("hook colon," 或"hook")
$dir = defined($ARGV[0]) ? shift(@ARGV) : "/tmp";
我们还可以写成下面这个方式,尽管语义稍微有点不一样:
$dir = @ARGV ? $ARGV[0] : "/tmp";
这个检查@ARGV包含的元素数量,因为第一个操作数@ARGV处于一个标量的上下文,它的布尔值为假仅当它包含了0个元素,而在它为布尔假值的情况下,我们才给$dir赋值"/tmp",在其他的情况下,(当用户给出一个参数,@ARGV不包含0个元素),我们给$dir赋值$ARGV[0]。
下面这一行让%count里面的某个值自增,用的key是$shell,如果$shell是布尔假值的话,用"/bin/sh"。
$count{ $shell || "/bin/sh" }++;
像下面这个例子一样,你可以把几种可以选择的值串起来。第一个返回真值的表达式将赋值给$user。
# find the user name on Unix systems
$user = $ENV{USER}
|| $ENV{LOGNAME}
|| getlogin( )
|| (getpwuid($<))[0]
|| "Unknown uid number $<";
&&操作符也是类似的,它返回第一个操作数如果这个操作数是假值。否则,它就返回第二个操作数。因为人们关心的假值往往没有人们关心的真值多,所以这个属性很少用到。13.12一节跟14.19一节对这个属性进行了一下演示。
||=赋值操作符看起来有点怪,但是它的赋值效果跟其他的二元赋值操作符一样。几乎所有Perl的二元赋值操作符,$VAR OP= VALUE 表示的意思就是$VAR = $VAR OP VALUE。举个例子,$a += $b效果跟$a = $a + $b一样。所以||= 是用来当一个变量本身的值为假值时设置该变量的值。由于||只是一个对布尔值的简单测试,所以它不关心这个值是不是已定义的,就算warning开关被打开了。
这里是一个||=的例子,除非starting_point本身有值,否则设置它为"Greenwich"。在这里我们再次假设$starting_point没有这些值: 0,"0", 如果它有这些值,这些值会被改掉的。
$starting_point ||= "Greenwich";
在赋值的时候你不能使用or来替换||, 因为or的优先级太小了。$a = $b or $c的效果是($a = $b) or $c。这并不是你想要的效果吧。
不要把||=跟||=在标量里面的运用扩展到数组跟哈希里面去。它是不适合的,因为这些操作符会把它们的左操作数放到一个标量的上下文中。你应该改成这样做:
@a = @b unless @a; # copy only if empty
@a = @b ? @b : @c; # assign @b if nonempty, else @c
Perl以后可能要支持新的操作符 //, //=, 跟err。这些定义或(defined-or)操作符会跟那些逻辑或操作符一样,只是它们测试的是是否已经定义,而不是布尔值。那样的话下面的几对代码作用是等价的。
$a = defined($b) ? $b : $c;
$a = $b // $c;
$x = defined($x) ? $x : $y;
$x //= $y;
defined(read(FH, $buf, $count) or die "read failed: $!";
read(FH, $buf, $count) err die "read failed: $!";
这3个操作符在Perl 5.9版本中已经有了,因为是一个奇数版本,所以只是一个实验性的版本,并不是你想要的成熟产品。还是期待5.10版本吧,肯定会有一个稳定的发行版,而且很可能在Perl 6.0版本种会具有这个特性,虽然6.0版的发行日期还没定。(译者注:遥遥无期?)
1.2.4 参阅
|| 操作符在perlop(1) 跟 Programming Perl 第3章里面有,defined 跟 exists 函数 在perlfunc(1) 跟 Programming Perl 第29章里面有。