Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6658601
  • 博文数量: 915
  • 博客积分: 17977
  • 博客等级: 上将
  • 技术积分: 8846
  • 用 户 组: 普通用户
  • 注册时间: 2005-08-26 09:59
个人简介

一个好老好老的老程序员了。

文章分类

全部博文(915)

文章存档

2022年(9)

2021年(13)

2020年(10)

2019年(40)

2018年(88)

2017年(130)

2015年(5)

2014年(12)

2013年(41)

2012年(36)

2011年(272)

2010年(1)

2009年(53)

2008年(65)

2007年(47)

2006年(81)

2005年(12)

分类: Python/Ruby

2011-06-08 22:25:28

=head1 NAME

Perl 交互环境(REPL read eval print loop)

=head1 DESCRIPTION

Python 带有一个 REPL 可以很方便的做计算器,或者做点稍微复杂的事情,作为 Perler
很是羡慕。Perl 源代码中带有个 psh 但是功能太简单了,就像它说的是“穷人的REPL”。
至于 perl -d 也不喜欢。于是搜了下 CPAN 结果没有找到合适的就自动动手写了个,
很简单。


=head2 Loop

REPL 最核心的就是 eval 函数和循环,Perl 都有只需要写个美化的界面如提示符什么的。直接看代码:

        sub prompt {
            my $pat = '[%03d]=> ';
            state $cnt = 0;
            printf $pat, $cnt++;
        }

这个是打印提示符的,带了行号。顺便 show 了下 state 关键字的用法。

        sub main {
            chdir 'd:/' or die $!;
            no strict;
            for(prompt; <>; prompt) {
                chomp;
                my @output = eval;
                say "\n=> ", @output if @output;
                print $@ if $@;
                say '';
            }
        }

主函数,首先切换到默认文件夹这个看个人喜欢我觉得挺方便。因为REPL中我们都懒得
声明变量,而且正常情况下都用全局变量所以就关闭了 strict。这里关于变量作用域
的问题很多,搞不懂的看仙子的教程。

循环很简单,打印提示符-> 读取一行代码-> list context eval -> B<打印返回值>。
至于这里的“一行代码”其实是由 $/ 决定的,所以不用担心要输入多行代码的问题。
只打印出返回值,所以下面的命令可以返回字符串来做命令提示。


=head2 辅助工具

上面是 psh 的全部了,我们还要添加些辅助功能否则和直接写 C 就没
多少区别了。

首先解决读入多行代码的问题:

        sub ml { $/ = "\n\n"; '[multi line mode ok]'  }
        sub sl { $/ = "\n";   '[line mode ok]'        }

在 REPL 中直接输入 ml 就切换为了多行模式就可以输入多行代码,最后用连续2个换行
就表示程序结束了。

改变当前目录也很常用,加上:

        sub ls { print "\n", `ls`; ''} # win 下用 dir 或自己写个 ls
        sub cd { chdir shift; cwd }

基本的提示信息:

        sub h { 'bla bla bla' }
        sub help { h() }


=head2 数据可视化

进行RE匹配的时候,构建复杂数据结构的时候经常需要将数据结果打印出来,但是
Perl 不会自动展开 reference 所以 Data::Dumper 就成了我的最爱。

        use Data::Dumper ();
        *D = \&Data::Dumper::Dumper;

这里把 Data::Dumper::Dumper 在 package main 中取了个别名,用一个字母 D 表示
是因为它实在太常用了。同样道理 yaml 格式有时也很常用,特别是很多匿名 hashref
的时候用 Data::Dumper 打印出来太长太乱了,这时候用 YAML 就非常舒服了。

        use YAML ();
        sub yaml { return "---- yaml ----\n", YAML::Dump @_ }

很简单,这里不用别名是为了打印输出时排版更漂亮。

有兴趣 hack Perl 内部数据结构的同学肯定很熟悉 Devel::Peek::Dump 同样默认加入。

        use Devel::Peek ();
        *DD = \&Devel::Peek::Dump;


=head2 测试模块

配合上面的辅助功能很容易测试 Perl 语言特性或者陷阱,但是有时候我们想要测试
类或者包的特性的时候就有点力不从心了,因为一般都需要个文件来帮忙。否则在
REPL 中反复输入都要累死了,但是 REPL 有点好处就是交互方便比 Test::More 简单
太多了。

下面我们有了 .pm 文件比如叫 T.pm 吧。放到当前目录用 use T; 来加载它,发现功能
不完善修改下,再 use T; 却发现不行了,提示说已经加载,头大啊难道还要再启用新
进程?

其实这是 Perl 的 require 加载机制决定了,加载的文件会放到 %INC 中,每次再
use 或 require 会检查加载没有,大家明白所在了吧(带有 XS 扩展的模块不行)。

目标就是删除 %INC 中的 'T.pm'。

        sub re_use {
            my $module = shift;
            $module =~ s/\s//g;
            $module =~ s/::/\//g;
       
            return if $module eq '';
       
            my $count = 0;
            my @packages;
       
            while (my($k, $v) = each %INC) {
       
                if ($k =~ m<^$module(?=[./])>) { #并不只删除此模块,是一系列模块
                    $count ++;
                    push @packages, $k;
                    delete $INC{$k};
                }
            }
            return "delete $count package\n\n", join "\n", @packages;
        }

注释那里这么说其实是因为模块命名都是有规律的,比如 Moose 所有辅助模块都在
Moose 文件夹下,这里一并删除就全部更新了。

这样我们就可以方便的写个模块,加载测试下-> 修改-> re_use 'Module_name' ->
加载继续测试。


=head2 来点高级的

Perl 的语法有时让人很迷惑,默认变量、作用域什么的,论坛上很多同学都问题多多。
用 B::Deparse 有时不失为一种很好的解决办法。这里只是简单封闭下,引号问题很多,
如果内容比较多还是老老实实用命令行解决吧。

        sub terse {
            my $code = shift;
            return "---- B::Terse ----\n", `perl -MO=Terse -E "$code"`;
        }
       
        sub deparse {
            my $code = shift;
            return "---- B::Deparse ----\n", `perl -MO=Deparse -E "$code"`;
        }


=head2 完善我们的计算器

为了方便做计算或测试,我们可以随意导入模块。有些不常用的也可以在 REPL 中动态
加载 比如 use LWP; 之类的。这里贴出最有用的

        use List::Util qw(first max maxstr min minstr reduce shuffle sum);
        use Scalar::Util qw(blessed dualvar isweak weaken refaddr reftype
        looks_like_number);

让退出更友善点

        $SIG{INT} = sub {say "\n*** goodbye ***"; sleep 0.5; exit};


=head1 总结

厌倦了总是在命令行 perl -E 就写了这么个小工具,慢慢的添加了很多小工具,这里
介绍了大部分有助于学习 Perl 语言的功能。因为只是简单的 eval 而且用的时候
经常是全局变量,如果遇到什么离奇的事我只能祝你 good luck 了。

至于应用其实它不仅仅是个简单的计算器、语言测试工具。因为可以动态加载模块,
我们完全可以创建一个文件,处理数据,输出;用 LWP 下载网页,处理,打印想要的
内容等等。配合着 Perl 完整的系统调用封闭和大量模块,它就是个 shell

给小鸟们提示下如果在 main 函数前声明了上面这些辅助函数,调用的时候就不需要
带括号了,更像命令了。



阅读(1769) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~