Perl 语言入门 ,第五版

取模操作符先取整然后在求余.

进行取模运算时, 如果其中一边或两边都是负数, 则不同的 Perl 版本可能会得出不同的结果.

你不能计算负数的负次方. (p.31)

除了单引号和反斜线字符外, 单引号内所有字符都代表他们自己(包括换行字符, 如果该字符串表示多行的数据的话)... 只有在后面是另一个反斜线或者单引号时, 前面的反斜线才有特殊意义. (p.32)
复制次数 (右操作数) 在使用前会先取整 (4.8 变成 4). 复制次数小于 1 时, 会生成长度为零的空字符串. (p.34)
字符串中非数字的部分 (以及前置的空白符号) 会被忽略, 所以 "12fred34" * "3" 也会得出 36, 而不会出现任何警告信息. 在最极端情况下, 完全不含数字的字符串会被转换成零.

要启动警告功能, 请在命令行运行程序时使用 -w 选项:

    $ perl -w my_program

或者, 如果 想要每次运行都启用警告功能, 可使用 #! 行来指明. (p.35)

在 Perl 术语里面, ($) 这种符号叫做 "印记" (sigil). (p.37)
全大写 (例如 $ARGV) 通常用来表示某种特殊变量. (p.38)
双目赋值操作符都是以某种方式直接修改变量的值, 而非对表达式求值后覆盖原变量值. (p.39)
如果变量从来没有被赋值过, 就会用空字符串来代替.

所有同事在 Perl 和 C 里出现的操作符, 他们的优先级和结合性都是相同的. (p.41)

如果字符串后面有两个以上的换行符, chomp 也仅仅删除一个. 如果结尾处没有换行符, 它什么也不做, 直接返回零. (p.46)
在首次被赋值之前, 变量的初始值都是特殊的 undef (未定义), 它在 Perl 里的意思只不过是: 这里空无一物 ... 把这个 "空无一物" 当成数字使用, 它就会假设这是 0, 如果当成字符串使用, 它就会假设这是空字符串. 但是 undef 既不是数字也不是字符串, 它完全是另一种类型的标量值. (p.47)
数组的名字空间 (namespace) 和标量的名字空间, 是完全分开的. (p.50)
如果下标超出数组的尾端, 则对应的值将会是 undef.

...

加入你对索引值超过数组尾端的元素进行赋值, 数组将会根据需要自动扩大. (p.51)

范围操作符 (range operator), ... 从左边的数字计数到右边, 每次加 1.

...

qw 表示 "quoted word" 或 "quoted by whitespace", ... Perl 都会将其当成单引号内的字符串来处理 (所以, 在 wq 构建的列表中, 不能像双引号内的字符串一样使用 \n 或 $fred). (p.52)

因为 qw 算是一种引号所以不能将注释放在 qw 列表中. (p.53)
对列表进行复制时, 出来的之会被悄悄忽略掉 ... 另一种情况, 如果变量的个数多余给定的列表值, 那么那些多出来的变量将会被设成 undef.

Larry 宣称他之所以选择 $ 与 @ 这两个符号, 是因为 $calar 看起来像 scalar (标量), 而 @rray 则像 array (数组). (p.54)

将某个数组复制到另一个数组时, 仍然算是列表的赋值运算, 只不过这些列表时存储在数组里而已. (p.55)
如果数组是空的, pop 什么也不做 (因为没有元素可供移出), 直接返回 undef.

push 的第一个参数, 或是 pop 的唯一的参数, 都必须是要操作的数组变量 --- 对列表直接量进行压入 (push) 或者 弹出 (pop) 操作是不可行的.

与 pop 类似, 对于一个空的数组变量, shift 会返回 undef. (p.56)

在循环中修改了控制变量的值, 也就同时修改了这个列表元素.

Perl 会自动存储 foreach 循环的控制变量, 并在循环结束之后还原. 在循环执行期间, 我们无法访问或者改变以存储的值, 所以当循环结束时, 变量仍然保持循环前的值; ... 也就是说, 假如你想将循环的控制变量取名为 $rock 的话, 不必担心是否用过同名的变量. (p.58)

假如你在 foreach 循环的开头省略了控制变量, Perl 就会使用它的 "老地方" 变量 $_.

当未告知 Perl 使用哪个变量或者数值时, Perl 都会自动使用 $_. (p.59)

sort 在标量上下文中总是返回 undef. (p.61)
reverse 在列表上下文中, 它很自然地返回逆序后的列表; 在标量上下文中, 则返回逆序后的字符串 (先将列表中所有的字符串全部连接在一起, 再对结果进行反序处理):

@backwards = reverse qw/ yabba dabba doo /

# 会变成 doo, dabba, yabba

$backwards = reverse qw/ yabba dabba doo /

# 会变成 oodabbadabbay (p.62)

@wilma = undef; # 糟糕! 结果是得到一个列表, 而且仅有的一个元素为未定义 (undef)

# 这和下面的做法效果完全不同

@betty = ( ); # 这才是正确的清空数组的方法

偶尔在列表上下文得地方, 你想要强制引入标量上下文, 这可以使用伪函数 scalar.

...

说来也奇怪, 没有相应的函数来强行切入列表上下文. 原因很简单, 因为你根本用不到, 相信我们. (p.63)

可以直接把数组交给 chomp, 它会自动去掉每个元素的换行符. (p.64)
假如你定义了两个重名的子程序, 那么后面的那个子程序会覆盖掉前面的. (p.67)
在 Perl 中, 所有的子程序都有一个返回值 --- 子程序并没有 "有返回值" 和 "没有返回值" 之分. 但并不是所有的 Perl 子程序都包含有用的返回值.

在子程序得执行过程中, 它会不断进行运算. 而最后一次运算得结果 (不管是什么), 都会被自动当成子程序得返回值. (p.68)

虽然 Perl 允许你省略语句块的最后一个分号, 但实际上通常只有在程序代码简洁到整个语句块只有一行时, 才有必要省略分号. (p.71)
my 操作符并不会改变变量赋值时的上下文:

my ($num) = @_; # 列表上下文, 和 ($num) = @_; 相同

my $num = @_; # 标量上下文, 和 $num = @_; 相同

请记住, 在 my 不使用括号时, 只用来声明单个词法变量:

my $fred, $barney; # 错! 没有声明 $barney

my ($fred, $barney); # 两个都声明了

"行输入" 操作符和 Perl 的 "老地方" 变量 ($_) 之间没有任何关联. (p.84)
因为 foreach 在循环能够开始之前, 它必须先将输入全部读进来, 因此最好的做法, 通常是尽量使用 while 循环的简写, 让它每次处理一行. (p.85)
使用钻石操作符时, 就好像这些文件已经合并成一个很大的文件.

可能你对 Unix 得一些东西还不熟悉: 大多数标准的工具, 它们都把连字符 (-) 当作标准输入流. (p.86)

由于钻石操作符通常会处理所有的输入, 所以当它在程序里出现好几次时, 通常是错误的.

假如钻石操作符无法打开某个文件并读入内容, 便会显示相关得出错诊断信息, 然后钻石操作符会自动跳到下一个文件, 就像 cat 或其他标准的工具程序的做法一样. (87)

printf 要输出真正的百分号, 请使用 %%, 它的特殊之处在于, 不会输出 (参数) 列表中的任何元素.

也许你认为在百分号前加个反斜线就行了. 不错的尝试, 但是不对. 这是因为这些格式符号是表达式, 而表达式 "\%" 就是代表字符串 '%' . 所以就算我们加了反斜线, printf 还是不知道该怎么做. (p.92)

Larry 也建议你使用权大写字母来命名文件句柄. (p.93)

my $selected_output = "my_output";

open LOG, "> $selected_output";

注意大于号后的空格. Perl 会忽略它, 但这个空格能防止意外发生. 如果 $selected_output 的值也是 ">passwd" 而之前有没有空格的话, 就会变成以替换方式写入, 而非以追加方式写入文件. (p.96)
当你重新打开某个文件句柄时(也就是说, 在新的 open 命令中重用之前的文件句柄名), Perl 会自动关闭原先的文件句柄. 在程序结束时, Perl 也会自动关闭文件句柄. (p.98)
将数据输出到文件句柄时, 默认情况下都会经过缓冲的处理. 不过, 只要将特殊变量 $| 设置为 1, 就会使当前的(也就是修改变量时所指定的)默认文件句柄在每次进行输出操作后, 立刻刷新缓冲区. (p.102)
哈希变量的命名和其他 Perl 的标识符相似, 可以有字母, 数字和下划线, 但不可以用数字开头. 另外哈希有自己的命名空间, 也就是说哈希元素 $family_name{"fred"} 和子程序 &family_name 之间毫无关联. (p.109)

%some_hash = ("foo", 35, "bar", 12.4, 2.5, "hello",

"wilma", 1.72e30, "betty", "bye\n");

在列表上下文中, 哈希会自动变成一些简单的键/值对:

@any_array = %some_hash;

我们把这个变换叫做哈希松绑, 将它变成键/值对的列表. 当然键/值对也不一定按照当初赋值的顺序松绑.

print "@any_array\n";

# 可能会给出像这样的结果:

# betty bye (以及一个换行) wilma 1.72e+30 foo 35 2.5 hello bar 12.4

之所以顺序乱掉是因为 Perl 已经为哈希的快速检索而对键/值对的存储做了特别的排序. 因此选择使用哈希的场合, 要么元素存储顺序无关紧要, 要么可以容易地在元素输出是进行排序.

当然, 即使键/值对的顺序被打乱, 列表里的每个键还是会带着相应的值. 所以, 即使无法知道某个键 foo 会出现在列表的哪个位置, 仍然可以确信相应的值 35 会跟在后面. (p.111)

在任何需要逗号的地方都可以用胖箭头代替, 这对 Perl 来说没什么区别. (p.112)
在标量上下文中, 这两个函数(keys 和 values)都会返回哈希中键/值对的个数. (p.113)
赋值后得到的列表, 在标量上下文中的求值结果为列表的成员数量.

...

每个哈希有自己的定位器, 因此处理不同的哈希的 each 调用可以嵌套. ... 使用 keys 或者 values 函数可以重置哈希的定位器. 另外用列表重置哈希内容时也可以重置定位器, 或者 each 调用遍历了整个哈希的时候也能重置. 然而在遍历哈希过程中增加新的键/值对就不太好, 因为这不会重置定位器, 反而会迷惑开发人员, 维护程序员, 另外还能愚弄 each. (p.114)

在 delete 之后, 键便不会出现在哈希之中, 但是存入 undef 之后键却一定会存在.

delete 与存入 undef 的差异, 就如同取消 Betty 的借书证与给她一张没用过的借书证一样完全不同. (p.116)

整个哈希的内插是不支持的, "%books" 的结果只是 6 个字符的串 %books. (p.117)
点号 (.) 是任何单字符的通配符, 当然换行 (也就是 "\n" ) 要除外. (p.121)
如何区分哪个括号是第几组?

...

只要数左括号 (包括嵌套括号) 的序号就可以了. (p.123)

[\d\D], 表示任何数字或非数字. 也就是说, 它会匹配任何字符! 这是匹配任意字符 (包括换行符) 的常见做法. (而点号 . 则匹配换行符以外的所有字符). p.128
在 Perl 里, 注释也算是一种空白.

...

如果在一个模式中使用多个修饰符, 可将它们连在一起使用. 它们之间的先后顺序并不会影响匹配的结果. (p.131)

美元符 ($) ... 除了匹配字符串的结尾之外, 它同时也匹配字符串结尾的换行符.

...

模式 /^fred$/ 会匹配 fred 与 fred\n 这两个字符串. (p.132) (注: \n 必须是结尾的换行)

\b 锚位匹配的是一组 \w 字符的开头或结尾. (p.133)
失败的匹配不会改变上次成功匹配时捕获的内容, 而成功的匹配会将它们重置. (p.136)
一旦你在程序中的任何部分使用了某个自动匹配变量, 其他正则表达式的运行速度会变慢. (p.141)
/g 修饰符可让 s/// 进行所有可能的、 不重复的替换.

...

不重复是因为每次都会从最近一次替换结束的地方开始新的匹配. (p.147)

split 是一个操作符, 虽然它的行为看来想一个函数, 而且大家通常都会称它为函数. (p.149)
split 会保留开头处的空字符, 并省略结尾处的空字段.
@fields = split /:/, ":::a:b:c:::" ;        # 得到 ("", "", "", "a", "b", "c")
...

split 默认会以空白字符分割 $_.

...

如果你用到更复杂的模式, 请避免在模式里用到捕获圆括号, 因为这回启动所谓的 "分隔保留模式模式". 在 split 里使用非捕获圆括号 (?:) 的写法, 就可以安全地进行分组. (p.150)

你可以把 join 的第一个参数理解为胶水, 它可以是任何字符串, 其余的参数则是一串片段. join 会把胶水涂进每个片段之间, 并返回结果字符串. (p.151)
my $x = join ":", 4, 6, 8, 10, 12;   # $x 为 "4:6:8:10:12" 
在列表上线文中使用模式匹配操作符 (m//) 时, 如果模式匹配成功, 那么返回的是所有捕获变量的列表; 如果匹配失败, 则返回空列表. (p.151)
在 Perl 中有 5 种循环块. 也就是 for、 foreach、 while、 until 以及裸块. 而 if 块或者子程序带的话括号不是循环的块.

...

last 操作符只会对运行中最内层的循环块发挥作用.

...

这可能是个坏主意, 但确实可以在子程序里用循环控制操作符来影响子程序外面的循环. 也就是说, 如果在循环块内调用一个子程序, 子程序内执行 last 操作, 同时子程序没有循环块, 那么程序的流程回跳到主程序循环块的后面. 在将来的 Perl 中, 这种在子程序内的循环控制能力会被去掉, 没有人会怀念它的. (p.171)

在 Perl 里, 标签和其他标识符一样由字母、 数字和下划线组成, 但是不能以数字开头.

...

Larry 建议用全大写字母命名标签, 这样不仅能防止跟其他标识符相互冲突, 也使得他们在程序中能突显出来. 无论大写还是小写, 标签总是罕见的, 只会在很少的 Perl 程序中出现. (p.173)

为了增进可读性, 通常的建议是把标签靠左, 哪怕当前的代码的层次缩进很深. 注意标签应该用来明明整块代码, 而不是用来标明程序中的某个具体位置. (p.174)

你可能意味 -T 和 -B 出现的结果必定相反, ... 但是, 有两种特殊情况会让测试结果相同: 如果文件不存在, 两者都会返回假, 因为它既不是文本文件也不是二进制文件; 而在空文件的情况下, 两者都会返回真, 因为它既是空的文本文件又是空的二进制文件. (p.195)
使用栈式写法时, 靠近文件名的测试会先执行, 次序为从右往左. (p.197)
use 5.010;
if (-r -w -x -o -d $file) {
print "My directory is readable, writeable, and executable!\n";
}
Perl 的 chdir 是通过操作系统实现的. (p.204)
任何在命令行上键入的模式, 都可以作为 (唯一的) 参数交给 glob 处理, 如果要一次匹配多种模式, 可以在参数中用空格分开各个模式. (p.205)
my @all_files_including_dot = glob ".* *";
readdir 操作符返回的文件名并不包含路径, 他们只是目录下的文件名而已.

...

许多古老的 Unix 程序都犯了这个错误, 以为 . 和 .. 一定是前两个返回的条目 (无论是否排序). (p.207)

在 Unix 上有个鲜为人知的事实: 某个文件可能你无法读取、 写入、 执行. 其实它根本就是别人的文件, 但你还是可以将它删除. 这是因为删除文件的权限跟文件本身的权限位无关, 它取决于文件所在的目录的权限位. (p.209)
相当成数字来用的字符串, 即使以 0 开头, 不会被解释成八进制数字.

...

oct 函数, 它能强行把字符串当成八进制数字处理, 无论是否以零开头. (p.216)

对非空的目录调用, rmdir 操作符会执行失败. (p.217)
习惯上会使用 1 来占个位子. (p.225)
1 while $number =~ s/^(-?\d+)(\d\d\d)/$1,$2/;  
因为性能需要, $a 和 $b 并不是数据项的拷贝, 它们实际上是原始列表的元素的临时化名. 所以, 如果我们改变他们的话, 就会弄乱原始的数据. 千万别这么做, Perl 不支持也不建议这种行为. (p.229)
my @numbers = sort { $a <=> $b } @some_numbers;

比较操作符 (<=> 与 cmp) 是短视的, 它们并不知道哪个操作数是 $a, 哪个操作数是 $b. 只知道哪一个值在左边, 哪一个在右边.

在 when 里调用某个函数 ... 取反的表达式, 包括取反的正则表达式, 都不会使用智能匹配模式. (p.241)
在 exec 调用之后写的任何代码都无法运行, 除非是编程接管启动过程中的错误: (p.249)
exec "date";
die "date couldn't run: $!"; 
修改从父进程继承的变量并不能影响 shell 或者其他父进行. (p.250)
在 反引号 (``) 中, 请勿使用会读取标准输入的命令. (p.252)
eval 其实是一个表达式, ... 因此在块的结束必须要分号. (p.261)
某些哈希键的引号可以省略.

...

这种不加引号的简单字符串被称为裸词, 因为它不需要引号.

...

胖箭头和逗号之间有一处重要的差异: 在胖箭头左边的裸词, 会被视为已加上引号. (p.265)

my @numbers = @names[ 9, 0, 2, 1, 0 ];  
当你看见类似 @names[ ... ] 的时候, 需要以 Perl 的习惯来看开头的符号和后面的方括号. 方括号意味着你要检索数组成员, @ 符号则意味着获取的是列表. (p.268)
但是有个切片可以工作、 列表却不能的场合. 那就是切片可以被直接内插到字符串总去.
my @names = qw{ zero one two three four five six seven eight nine } ;
print "Bedrock @names[ 9, 0, 2, 1, 0 ]\n";  
my @three_scores = ($score{"barney"}, $score{"fred"}, $score{"dino"});
my @three_scores = @score( qw/ barney fred dino / );
切片一定是列表, ... 如果看到类似 @names{ ... } 的代码, 你需要以 Perl 的习惯来看开头的符号和后面的花括号. 花括号意味着你要检索哈希, @ 符号意味着获取的是列表. (p.269)