pure function

A pure function:

Has no side effects

Return value depends only on arguments

Example:

sub factorial {
my $n = shift;
$n == 0 ? 1 : $n * factorial($n-1);
}

cache 只对 pure function 有意义

这可以思考查询一个档案的修改时间的这种 function,他并非一个 pure function,所以对非 pure function cache 是没有意义的这里有一个例子

Highly Recursive Functions
sub fib {
my ($n) = @_;
return $n if $n ==0 || $n == 1;
return fib($n-2) + fib($n-1);
}

This function is very very slow!

Caching Fixes Recursion
Solution: Caching

@fib = (0, 1);
sub fib {
my ($n) = @_;
return $fib[$n] if defined $fib[$n];
return $fib[$n] = fib($n-1) + fib($n-2);
}

fib(20) computes fib(18) and fib(19)

fib(19) goes to compute fib(18)

But it is already in $fib[18]

Function is now very fast,Almost as fast as a pure iterative version,
Unlike the iterative version, this version required no ingenuity

以上文章节录自 http://perl.plover.com/yak/hw-dcpm/

Perl 变量魔法

先来看一段 Perl 脚本:

sub showVars {
 
    
my $name = "first";
    
print    $name"\t at " . \$name . "\n";
    
for(my $i = 0; $i < 2; $i++) {
        
my $name = "loop_" . $i;
        
if (1 < 3) {
            
print    $name"\t at " . \$name . "\n";
            
my $name = "if_A";
            
print    $name"\t at " . \$name . "\n";
            
{
                
my $name = "blank";
                
print    $name"\t at " . \$name . "\n";
            
}
            
print    $name"\t at " . \$name . "\n";
        
}
        
if (1 < 4) {
            
print    $name"\t at " . \$name . "\n";
            
my $name = "if_B";
            
print    $name"\t at " . \$name . "\n";
        
}
        
print    $name"\t at " . \$name . "\n";
    
}
    
print    $name"\t at " . \$name . "\n";
}
 
sub showVars_2 {
 
    
my $name = "first";
    
print    $name"\t at " . \$name . "\n";
    
for(my $i = 0; $i < 2; $i++) {
        
$name = "loop_" . $i;
        
if (1 < 3) {
            
print    $name"\t at " . \$name . "\n";
            
$name = "if_A";
            
print    $name"\t at " . \$name . "\n";
            
{
                
$name = "blank";
                
print    $name"\t at " . \$name . "\n";
            
}
            
print    $name"\t at " . \$name . "\n";
        
}
        
if (1 < 4) {
            
print    $name"\t at " . \$name . "\n";
            
$name = "if_B";
            
print    $name"\t at " . \$name . "\n";
        
}
        
print    $name"\t at " . \$name . "\n";
    
}
    
print    $name"\t at " . \$name . "\n";
}
 
showVars();
print    "--------------\n";
showVars_2();

这里面有 2 个函数,两者之间的区别,就是变量的定义方式不同。

第一个函数,多次使用 my 声明变量。Perl 会在每一个局部区域内,比如说大括号 {} 内,实现一个命名空间。
也就是说,在不同的代码段,相同的变量名,实际上指向了不同的内存地址。
第一个函数体内,首先声明了一个变量 $name,他的作用域是整个函数,但在 for 循环体内,又用 my 声明了 $name 变量,因为 for 是一个封闭的结构,在 {} 里面就可以产生一个新的命名空间,此时 $name 指向了一个新的内存地址。这一点可以在后面的输出结果中看到。

由此,引发一个值得思考的问题:
如果在循环体内部用 my 去声明变量,那么循环执行多少次,就会有多少个变量的副本,这会浪费很多内存空间。
这一点是跟其他类 C 语言是有很大区别的。
所以,如果你要想利用 Perl 的命名空间,那么只要多注意代码段的作用范围,编程的时候能感受到 Perl 十分强大的便利性。
需要注意的,就是避免在循环体中使用 my 声明,浪费内存空间不说,还会影响程序的性能。

说了半天,还是看一看实际的结果吧:

$ perl show_vars.pl
first    at SCALAR(0x182fb10)
loop_0   at SCALAR(0x182fbb8)
if_A     at SCALAR(0x182fc24)
blank    at SCALAR(0x182fc84)
if_A     at SCALAR(0x182fc24)
loop_0   at SCALAR(0x182fbb8)
if_B     at SCALAR(0x182fd5c)
loop_0   at SCALAR(0x182fbb8)
loop_1   at SCALAR(0x182fd5c)
if_A     at SCALAR(0x182fc84)
blank    at SCALAR(0x276164)
if_A     at SCALAR(0x182fc84)
loop_1   at SCALAR(0x182fd5c)
if_B     at SCALAR(0x182fc24)
loop_1   at SCALAR(0x182fd5c)
first    at SCALAR(0x182fb10)
--------------
first    at SCALAR(0x182fe88)
loop_0   at SCALAR(0x182fe88)
if_A     at SCALAR(0x182fe88)
blank    at SCALAR(0x182fe88)
blank    at SCALAR(0x182fe88)
blank    at SCALAR(0x182fe88)
if_B     at SCALAR(0x182fe88)
if_B     at SCALAR(0x182fe88)
loop_1   at SCALAR(0x182fe88)
if_A     at SCALAR(0x182fe88)
blank    at SCALAR(0x182fe88)
blank    at SCALAR(0x182fe88)
blank    at SCALAR(0x182fe88)
if_B     at SCALAR(0x182fe88)
if_B     at SCALAR(0x182fe88)
if_B     at SCALAR(0x182fe88)

PHP Perl 关联数组 哈希表 Hash Table

关联数组,又称为哈希表(hash table),是一种非常好用的数据结构。

在程序中,我们可能会遇到需要消重的问题,举一个最简单的模型:

有一份用户名列表,存储了 10000 个用户名,没有重复项;
还有一份黑名单列表,存储了 2000 个用户名,格式与用户名列表相同;
现在需要从用户名列表中删除处在黑名单里的用户名,要求用尽量快的时间处理。

这个问题是一个小规模的处理量,如果实际一点,2 个表都可能很大,比如有 2 亿条记录。

我最开始想到的方法,就是做一个嵌套的循环,设用户名表有 M 条记录,黑名单列表有 N 条记录,那么,循环的次数是 M * N 次!
PHP 版代码:

<?php
foreach($arrayM as $keyM => $nameM) {
    
foreach($arrayN as $nameN) {
        
if ($nameM == $nameN) {
            
// 本行执行了 M * N 次!
            
unset($arrayM[$keyM]);
        
}
    
}
}
return $arrayM;
?>

另一种方式,利用数组索引。

PHP 是一种弱类型的语言,不像 C 语言那样有严格的变量类型限制。C 语言的数组,每一个元素的类型必须一致,而且索引都是从 0 开始。
PHP 的数组,可以用字符串作为索引,也称为关联数组。
数组索引,有一个天然的限制就是不会重复,而且访问的时候不需要查找,可以直接定位。

还是刚才的那个问题,我们采用另一种办法。

把黑名单列表的用户名组织到一个数组里,数组的索引就是用户名。

然后,遍历用户列表的时候,只需直接用 isset 查询那个用户名是否存在即可。

PHP 版代码:

<?php
$arrayHash = array();
foreach($arrayN as $nameN) {
    
// 本行执行了 N 次。
    
$arrayHash[$nameN] = 1;
}
 
foreach($arrayM as $keyM => $nameM) {
    
if (isset($arrayHash[$nameM])) {
        
// 本行执行了 M 次!
        
unset($arrayM[$keyM]);
    
}
}
return $arrayM;
?>

可以看到,优化过的代码,循环次数是 M + N 次。

假如 M 和 N 都是 10000,优化前,循环了 1 亿次;优化后,只循环了 20000 次,差了 5000 倍!
如果第二个程序耗时 1 秒,则第一个程序需要将近一个半小时!

最近在做 Perl 的开发,Perl 在处理文本的时候有很高的效率,同样,它也支持关联数组!

只是语法和 PHP 的那种类 C 的方式有很大不同,以第二段代码为例,Perl 版的实现:

#!/usr/bin/perl
my %arrayHash;
for(my $i = 0; $i < @arrayN; ++$i) {
    
$arrayHash{$arrayN[$i]} = 1;
}
 
for(my $i = 0; $i < @arrayM; ++$i) {
    
if ($arrayHash{$arrayM[$i]}) {
        
$arrayM[$i] = undef;
    
}
}

Perl 的数组是 @ 开头,哈希是以 % 开头,unset 实际上就是 undef。
Perl 的哈希和数组都是有具体类型的,而且向函数传递变量的时候要传引用,我刚学时间不长,快被搞晕了。

不过,现在刚刚实现了一个以 hash 方式进行 IP 位置查找的算法,平均比较次数大概在 3 次左右,比传统的折半查找方式少了很多次,它大概需要 8 次以上的比较。

刚刚做了一个小的性能测试,对 10 万个 IP 进行查找,在我的台式机上,耗时 15 秒,平均每秒 7500 次,感觉还不错,呵呵。

不过,还是喜欢 PHP 的数组,真的很强大!

God Bless PHP!

Google

Google
LAMP-Linux-redhat LAMP-Apache LAMP-MySQL LAMP-Php Leakon-Wiki Leakon-BBS XueBaoBao Xyoyou