Sky对《Perl语言入门 第六版(中文版)》的笔记(94)

Sky
Sky (好好读书,认真笔记!)

读过 Perl语言入门 第六版(中文版)

  • 第121页 访问整个哈希

    要指代整个hash,可以使用%作为前缀。这个可以类比一下指代整个数组或者列表使用@作为前缀。 利用这种指代方式,可以实现Hash和list的互转。

    # convert list to hash
    %some_hash=('foo',35,'bar',12.4,2.5,'hello','wilma',1.72e30,'betty',"bye\n");
    # 上面的语句也相当于hash的初始化了。相当于下面的5行
    $some_hash{'foo'} = 35;
    $some_hash{'bar'} = 12.4;
    $some_hash{'2.5'} = 'hello';
    $some_hash{'wilma'} = 1.72e30;
    $some_hash{'betty'} = "bye\n";
    # 如果list中的元素是奇数个,这个过程会怎样呢?会报错吗?
    # 看下面的这个模拟程序
    %hash_test = ('a','b',123);
    foreach my $key (keys %hash_test){
    	print "$key => $hash_test{$key}\n";
    	print "${key}'s value is undefind!\n" unless defined $hash_test{$key};
    }
    # 在win下直接执行perl xx.pl的结果如下:
    123 =>
    123's value is undefind!
    a => b
    # 在win下执行perl -w xx.pl的结果发现,虽然程序没有抛错终止,但是还是给出了warning信息的:
    Odd number of elements in hash assignment at test.pl line 1.
    Use of uninitialized value in concatenation (.) or string at test.pl line 3.
    123 =>
    123's value is undefind!
    a => b
    
    # convert hash to list
    @any_array = %some_hash;
    # 这样得到的列表会类似上面的hash赋值用的列表。但是不保证KV对的顺序,因为Perl为了优化哈希,已经对hash进行了特别排序。但KV的对应关系不会被打乱。
    哈希赋值
    引自 访问整个哈希
    my %new_hash = %old_hash;
    my %inverse_hash = reverse %any_hash;    #这个过程会将原来的%any_hash中的KV全部反转过来,赋值到新的%inverse_hash中。这里如果出现%inverse_hash中key重复的情况,后面的会覆盖掉前面的。
    2012-11-20 18:25:07 回应
  • 第123页 胖箭头(=>)

    在Perl中,胖箭头(=>)几乎可以等同于逗号(但是当胖箭头左边的任何裸字都会被加上引号),于是在之前提到的list转换为hash的场景中,使用胖箭头的可读性,就会好很多。

    my %last_name = (
        fred => 'flintstone',    # 注意,fred会自动被套上引号,于是就是合法的字符串了。
        dino => undef,
        barney => 'rubble',
        betty => 'rubble',    # 最后的这个多余的逗号,Perl不会在意,也不会报错,这个和很多编程语言不太一样。
    )
    # 上面的这个你排成一排也完全没问题。

    哈希key的简写形式。 因为哈希key必定是个字符串,所以引用hash里面特定值的时候,可以省略引号了。

    $some_hash{'fred'};
    $some_hash{ fred };    # 和上面一句等价
    $some_hash{bar.foo};    # 等价于 $some_hash{'barfoo'}; 
    2012-11-20 19:01:07 回应
  • 第124页 哈希函数
    keys和values函数
    引自 哈希函数
    my %hash = (a=>1,b=>2,c=>1);
    my @keys = keys %hash;
    my @values = values %hash;
    print 'keys of the %hash are : '."@keys\n";
    print 'values of the %hash are : '."@values\n";
    my $count1 = keys %hash;
    my $count2 = values %hash;
    print "scalar form of keys = $count1\n";
    print "scalar form of values = $count2\n";
    #上面程序的输出结果如下:
    keys of the %hash are : c a b
    values of the %hash are : 1 1 2    #注意这里重复的value也会按照跟key对应的顺序输出。
    scalar form of keys = 3
    scalar form of values = 3
    
    ##一种不太常见,但是也有使用场景的,对hash判空的操作
    if (%hash) {
        print '%hash is not empty!';
    }
    each函数
    引自 哈希函数

    实际使用中,唯一适合each的地方,就是在while循环里。

    while ( ($key,$value)=each %hash ) {    #each的这种写法,又让我想起了PHP。。。但是从Perl的角度出发来看的话,while的求职表达式是需要的布尔值,就是一个特殊的标量上下文,于是就是最终相当于列表元素的数量,这里如果each遍历的不为空的话,列表($key,$value)的元素的数量就是2,因为2是真值,所以打印下面的输出。
        print "$key => $value\n";
    }

    2012-11-20 19:40:20 回应
  • 第127页 哈希的典型应用
    exists函数
    引自 哈希的典型应用
    if ( exists $some_hash{"dino"} ) {    #注意,虽然$some_hash{"dino"}看上去像是获取dino对应的值,但是这个判读其实跟值没有半毛钱关系,主要是check dion是否作为一个key存在于$some_hash中。
        print 'dino exists as a key in $some_hash';
    }
    delete函数
    引自 哈希的典型应用
    my $person = "betty";
    delete $books{$person};    #删除person作为key对应的那条记录
    哈希元素内插
    引自 哈希的典型应用

    注意这里所说的是哈希元素的内插,整个哈希元素还不支持内插,这个和list和array有一些区别。

    foreach my $person (sort keys %books) {
        if ($books{$person}) {
             print "$person has $books{$person} items\n";
        }
    }
    %ENV哈希
    引自 哈希的典型应用

    这个hash主要用于存放系统的环境变量。环境变量本身就是KV形式,这样访问也很方便。在Perl程序中,就可以利用%EVN来得到这些变量的值。

    print "PATH is $ENV{PATH}\n";
    # the output in my OS is as following:
    PATH is /usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin
    2012-11-20 20:06:21 回应
  • 第133页 关于元字符

    主要就是下面这几个了:

    .    #万能元字符,正则中匹配任何字符,但是!!!换行符\n除外!
    \    #元字符的转义字符,比如常用的:\.  \\
    ()   #小括号主要用于正则中的分组
    简单的量词
    引自 关于元字符
    *   # 出现次数:>=0
    +  # 出现次数:>=1
    ?  # 出现次数:0 或 1
    2012-11-21 14:07:55 回应
  • 第134页 模式分组

    前面提过,小括号()在正则表达式中,是用户字符串分组,有了这个分组,也使得我们有机会通过反向引用(back reference)来捕获组(capture group)。

    $_ = "abba";
    if (/(.)\1/) {    #"bb" is matched.
        print "It matched same character next to itself!\n";
    }
    
    $_ = "yabba dabba doo";
    if ( /y(....) d\1/ ) {
        print "It matched the same after y and d!\n";
    }
    
    $_ = "yabba dabba doo";
    if ( /y((.)(.)\3\2) d\1/ ) {    #来个复杂一点的,注意反向引用组的编号是从左边开始数左括号。所以这里的\1其实对应的是最外层的小括号。
        print "It matched!\n";
    }
    
    $_ = "aa11bb";
    if ( /(.)\111/ ) {    #这是一个很好的例子,Perl解释这种有二义性的表达式的时候,原则是尽可能创建最多数量的反向引用。比如:这里会被解释成\111,即,第111个反向引用。
    	print "Matched!\n";
    } else {    #这里输出的是"Not Match!"
    	print "Not Match!\n";
    }
    
    #对于上面的例子,如果你期望的效果应该是\1这个样子的,那么Perl5.10之后提供的\g{N}的表达式更符合你的要求。
    use 5.010;
    
    $_ = "aa11bb";
    if ( /(.)\g{1}11/ ) {    #这次输出的就是"Matched!"了,因为匹配中了"aa11".
    	print "Matched!\n";
    } else {    
    	print "Not Match!\n";
    }
    
    相对反向引用
    引自 模式分组

    这个用法比较灵活。就是针对上面提到的\g{N}这种形式。当N为负整数时,反向引用使用相对于最靠近\g{N}的分组开始往回数的第N个分组。

    use 5.010;
    $_ = "xax11bb";
    if ( /(.)(.)\g{-2}11/ ) {    #这里的\g{-2}就相当于从左往右数的第一个小括号,所以"xax11"符合匹配。
    	print "Matched!\n";    #Go to here.
    } else {
    	print "Not Match!\n";
    }
    2012-11-21 15:38:27 回应
  • 第137页 择一匹配

    正则表达式中,竖线(|)通常可以被当作“或”的关系。 具体区分一下下面的这两种使用方式:

    /fred( |\t)+barney/    #这个pattern会匹配fred和barney之前出现一次以上的空格,或者制表符或者两者的混合的字符串。
    #如果你的本意并不希望出现空格和制表符混合的情况,那就需要改成下面这个样子了。
    /fred( +|\t+)barney/
    字符集
    引自 择一匹配

    这个需要注意的就是,[]中只匹配单个的字符,但是可以是里面列出的任何一个。

    [abcde]    #表示匹配a、b、c、d、e中的任何一个
    [a-e]    #和上面匹配的效果一样,但是使用了连字符(-)做简化
    [^n\-z]    #匹配除n、-、z以外的任何字符。这里注意:连字符在字符集[]中具有特殊的含义,所以需要反斜杠进行转义,类似的如果是字符^在[]中不作为脱字符使用,也需要进行转义。
    字符集的简写
    引自 择一匹配

    在早期的ASCII码字符集的时候,\d \s \w 这种简写形式非常简洁,语义也很明确。但是Perl引入Unicode之后,这些简写形式就被无形中扩大了。 Perl5.14之后,才引入了一个修饰符/a,写在正则表达式末尾,用于将上面提到的这些简写形式的语义还是限定在ASCII范围内。

    反义简写
    引自 择一匹配

    这里很好理解,\D \W \S就是分别相当于[^\d] [^\w] [^\s] ,这里需要注意的是,书里列举了两个比较变态的例子。

    [\d\D]    #表示任意数字和非数字,所以就是任何字符
    [^\d\D]    #表示既不是数字,也不是非数字,所以就是不匹配任何字符
    2012-11-21 17:16:42 回应
  • 第142页 课后习题

    这里记录一下第6题的答案,因为有个小弯。

    写个程序,输出在输入数据中同时出现wilma以及fred的每一行
    引自 课后习题

    这个问题稍微转弯的地方就是只要wilma和fred同时出现就行了。谁在前,谁在后都没所谓。

    while (<>) {
        if(/fred/){
            if(/wilma/){
                print;
            }
        }
    }
    #上面的实现其实也挺巧秒的,但是pattern被分成了两次匹配,不够简洁。要一次搞定,那就到下面这个了
    while(<>) {
        if( /fred.*wilma|wilma.*fred/ ){    #这个pattern还真不难,不过自己凭空想的时候,很容易把或(|)这个给忘记了
            print;
        }
    }
    2012-11-21 18:16:29 回应
  • 第144页 模式匹配修饰符
    /a\wb/a    #之前提到过的,可以使\d \s \w 仅限定在ASCII码范围内。
    /yse/i       #/i表示忽略大小写
    /Barney.*Fred/s    #/s会扩大pattern中的匹配范围(单纯的.是不能匹配\n的,加了/s之后就可以了。)
    / -? [0-9]+ \.? [0-9]* /x    #/x作用是将pattern中空格全部忽略,甚至可以忽略pattern中的注释符号,比如下面的例子
    / 
    -?        #一个可有可无的减号
    [0-9]+  #至少一个数字
    \.?       #一个可有可无的小数点
    [0-9]*  #0到多个数字
    /x
    2012-11-21 22:16:11 回应
  • 第146页 选择一种字符解析方式

    下面所列出来的3种字符解析方式,仅适用于Perl5.14+的版本!

    use 5.014;
    /\w+/a    #ASCII码中的字符,相当于[a-zA-Z0-9_]
    /\w+/u    #任何在Unicode中定义为单词的字符
    /\w+/l     #类似ASCII的版本,但是可能会由于本地化设置比ASCII的范围大一些

    另外,关于Perl中正则的忽略大小写的选项/i,还有个跟aa组合的用法。这个问题又涉及到怎么忽略大小写的实现:Perl要忽略大小写,首先就得知道待匹配单词的大小写分别是啥?书里举例是k,在ASCII范围内没有任何问题,大写形式肯定就是大写的英文字母K了。但编码范围如果扩展Unicode之后,会发现一个跟大写K一模一样的字符,是温度里面Kelvin这个单位,但是显然他的Unicode编码和大写字母K的Unicode编码是不同的。于是就出现了根据大写K可对应到小写k,但是根据小写k没法找对应回大写K的情况(因为有两个K)——相当于数学里面的函数定义问题嘛。

    #下面三种的写法是等效的,都是在ASCII范围内,忽略大小写匹配k
    /k/iaa
    /k/aia
    /k/aai

    关于/l,书里讲了一个很令人头大的例子,没怎么看懂,先记一下吧,回头再看看。

    $_ = <STDIN>;
    my $OE = chr (0xBC);    #明确取得我们所讲的那个字符
    if (/$OE/i) {                     #大小写无关?未必!书里建议写成这样:/$OE/li
        print "Found $OE\n";
    }
    2012-11-22 16:59:27 回应

Sky的其他笔记  · · · · · ·  ( 全部124条 )