| 订阅 | 在线投稿
分享
 
 
 

功能丰富的Perl:编写说英语的Perl程序

来源:互联网  宽屏版  评论
2008-05-19 06:25:54

设计程序的用户界面可能很困难而且耗时。Teodor Zlatanov 讨论了如何使用 Parse::RecDescent 模块来用简单的英语创建用户界面文法。他还展示了向程序添加功能或从程序除去功能时,更改文法是如何的方便。另外还与标准的 CLI 解析器和 GUI 进行了比较,讨论了这种方法的优缺点。

随功能一起发展的很棒的用户界面

因为用户界面是程序最初的入口,所以它必须能够用于多种目的。必须向用户提供对程序所有功能的合适访问。在向程序添加更多功能时(这几乎是必然发生的情况),它必须是可扩展的。必须具备灵活性,可以接受常用命令的缩写和快捷方式。它不应该有层叠的菜单或瀑布式单词,这样会让用户感到迷惑。无可否认,以上所有这些要求对程序员来说都是复杂的约束,对此没有一种很好的解决方案能把它们全包括。许多软件产品开发人员到最后再解决用户界面问题,把它作为一种事后来考虑的问题。另外一些开发人员则首先主要考虑用户界面,让功能仅仅成为界面设计选择的结果。这些都不是理想的方法。用户界面(UI)应该随着程序功能的发展而发展,两者就象一枚硬币的正反面。

这里我们将面向解析的方法用于用户界面。虽然这种方法适合于 GUI 界面,但本文不讨论 GUI 设计。我们将专门讨论基于文本的 UI。首先,将简要介绍标准的文本 UI 设计选择,使您能熟悉该环境。然后,将展示 Parse::RecDescent 解决方案,事实证明它是灵活、直观和易于编写的!

注:为了运行我们所讨论的某些程序,将需要 Parse::RecDescent CPAN 模块。

用传统的 Unix 方式创建的简单用户界面

Unix 用户非常熟悉基于文本的 UI 模型。设想有一个 Perl 程序,让我们先看一下这个模型用于该程序的简单实现。标准的 Getopt::Std 模块简化了命令行参数的解析。这个程序仅仅为了说明 Getopt::Std 模块(没有实际用途)。请参阅本文后面的参考资料。

使用 Getopt::Std 的命令行开关

#!/usr/bin/perl -w

use strict;# always use strict, it's a good habit

use Getopt::Std;# see "perldoc Getopt::Std"

my %options;

getopts('f:hl', \%options);# read the options with getopts

# uncomment the following two lines to see what the options hash contains

#use Data::Dumper;

#print Dumper \%options;

$options{h} && usage();# the -h switch

# use the -f switch, if it's given, or use a default configuration filename

my $config_file = $options{f} || 'first.conf';

print "Configuration file is $config_file\n";

# check for the -l switch

if ($options{l})

{

system('/bin/ls -l');

}

else

{

system('/bin/ls');

}

# print out the help and exit

sub usage

{

print

first.pl [-l] [-h] [-f FILENAME]

Lists the files in the current directory, using either /bin/ls or

/bin/ls -l.

The -f switch selects a different configuration file.

The -h switch prints this help.

EOHIPPUS

exit;

}

简单的事件循环

当命令行参数不够用时,下一步是编写一个事件循环。在这种方案中,仍然可接受命令行参数,并且有时只使用命令行参数就足够了。然而,事件循环支持用户在不输入任何参数的情形下调用程序,以及看到提示符。在此提示符下,通常可使用 help 命令,这将打印出更详细的帮助。有时,这个 help 甚至可以是一个单独的输入提示符,有一个完整的软件子系统来专门负责它。

带有命令行开关的事件循环

#!/usr/bin/perl -w

use strict; # always use strict, it's a good habit

use Getopt::Std; # see "perldoc Getopt::Std"

my %options;

getopts('f:hl', \%options); # read the options with getopts

# uncomment the following two lines to see what the options hash contains

#use Data::Dumper;

#print Dumper \%options;

$options{h} && usage(1); # the -h switch, with exit option

# use the -f switch, if it's given, or use a default configuration filename

my $config_file = $options{f} || 'first.conf';

print "Configuration file is $config_file\n";

# check for the -l switch

if ($options{l})

{

system('/bin/ls -l');

}

else

{

my $input; # a variable to hold user input

do

{

print "Type 'help' for help, or 'quit' to quit\n- ";

$input = ;

print "You entered $input\n"; # let the user know what we got

# note that 'listlong' matches /list/, so listlong has to come first

# also, the i switch is used so upper/lower case makes no difference

if ($input =~ /listlong/i)

{

system('/bin/ls -l');

}

elsif ($input =~ /list/i)

{

system('/bin/ls');

}

elsif ($input =~ /help/i)

{

usage();

}

elsif ($input =~ /quit/i)

{

exit;

}

}

while (1); # only 'quit' or ^C can exit the loop

}

exit; # implicit exit here anyway

# print out the help and exit

sub usage

{

my $exit = shift @_ || 0; # don't exit unless explicitly told so

print

first.pl [-l] [-h] [-f FILENAME]

The -l switch lists the files in the current directory, using /bin/ls -l.

The -f switch selects a different configuration file.

The -h

switch prints this help.

Without the -l or -h arguments, will show

a command prompt.

Commands you can use at the prompt:

list:

list the files in the current directory

listlong:

list the files in the current directory in long format

help:

print out this help

quit:

quit the program

EOHIPPUS

exit if $exit;

}

这里,通常会有三种选择:

由于可能会有多个开关组合,所以程序的 UI 会复杂到不可忍受的程度。

UI 将发展为 GUI。

从头重新开发 UI,使其至少带有一些解析功能。

第一种选择太可怕,难以去想象。这里不讨论第二种选择,但它的确在向后兼容性和灵活性方面提出了有趣的挑战。第三种选择是本文下面要讨论的内容。

Parse::RecDescent 的快速教程

Parse::RecDescent 是一个用于解析文本的模块。通过几个简单构造就可以用它完成几乎所有的解析任务。更高级的文法构造可能会让人望而生畏,不过在大多数用途中不需要这么高级的文法。

Parse::RecDescent 是一个面向对象的模块。它围绕着文法创建解析器对象。文法(grammar)是一组以文本形式表示的规则。下面这个示例是一条匹配单词的规则:

word 规则

word: /\w+/

这条规则一次或多次匹配字符(\w)。跟在冒号后的部分称为产品(production)。一条规则必须包含至少一个产品。一个产品可以包含其它规则或直接要匹配的内容。下面这个示例是一条规则,它可以匹配一个单词、另一条规则(non-word)或一个错误(如果其它两个都失败):

另一些产品

token: word | non-word |

word: /\w+/

non-word: /\W+/

每个产品也可以包含一个用花括号括起的操作:

产品中的操作

print: /print/i { print_function(); }

如果操作是产品中的最后一项,则操作的返回码决定产品是否成功。该操作是一种空产品,它将总是会匹配,除非它返回 0。

可以用 (s) 修饰符指定多个标记(token):

一个产品中带一个或多个标记

word: letter(s)

letter: /\w/

也可以将 (?)(0 或 1)和 (s?)(0 到 N)修饰符作为可选关键字来使用。

可以通过 $item[position] 或 $item{name} 机制来访问产品中的任何内容。请注意,在第二种情形中,两个单词的名称是相同的,所以必须使用位置查找。在第三种情形中,单词数组以数组引用的形式存储在 $item{word} 中。如果在产品中使用可选项,则数组定位方案将肯定无法很好地工作。一般来说,无论如何都应避免使用这种方案,因为通过名称查找的方式总是更方便更简单:

使用 %item 和 @item 变量

print: /print/i word { print_function($item{word}); }

print2: /print2/i word word { print_function($item[1], $item[2]); }

print3: /print3/i w

  设计程序的用户界面可能很困难而且耗时。Teodor Zlatanov 讨论了如何使用 Parse::RecDescent 模块来用简单的英语创建用户界面文法。他还展示了向程序添加功能或从程序除去功能时,更改文法是如何的方便。另外还与标准的 CLI 解析器和 GUI 进行了比较,讨论了这种方法的优缺点。   随功能一起发展的很棒的用户界面   因为用户界面是程序最初的入口,所以它必须能够用于多种目的。必须向用户提供对程序所有功能的合适访问。在向程序添加更多功能时(这几乎是必然发生的情况),它必须是可扩展的。必须具备灵活性,可以接受常用命令的缩写和快捷方式。它不应该有层叠的菜单或瀑布式单词,这样会让用户感到迷惑。无可否认,以上所有这些要求对程序员来说都是复杂的约束,对此没有一种很好的解决方案能把它们全包括。许多软件产品开发人员到最后再解决用户界面问题,把它作为一种事后来考虑的问题。另外一些开发人员则首先主要考虑用户界面,让功能仅仅成为界面设计选择的结果。这些都不是理想的方法。用户界面(UI)应该随着程序功能的发展而发展,两者就象一枚硬币的正反面。   这里我们将面向解析的方法用于用户界面。虽然这种方法适合于 GUI 界面,但本文不讨论 GUI 设计。我们将专门讨论基于文本的 UI。首先,将简要介绍标准的文本 UI 设计选择,使您能熟悉该环境。然后,将展示 Parse::RecDescent 解决方案,事实证明它是灵活、直观和易于编写的!   注:为了运行我们所讨论的某些程序,将需要 Parse::RecDescent CPAN 模块。   用传统的 Unix 方式创建的简单用户界面   Unix 用户非常熟悉基于文本的 UI 模型。设想有一个 Perl 程序,让我们先看一下这个模型用于该程序的简单实现。标准的 Getopt::Std 模块简化了命令行参数的解析。这个程序仅仅为了说明 Getopt::Std 模块(没有实际用途)。请参阅本文后面的参考资料。   使用 Getopt::Std 的命令行开关   #!/usr/bin/perl -w   use strict; # always use strict, it's a good habit   use Getopt::Std; # see "perldoc Getopt::Std"   my %options;   getopts('f:hl', \%options); # read the options with getopts   # uncomment the following two lines to see what the options hash contains   #use Data::Dumper;   #print Dumper \%options;   $options{h} && usage(); # the -h switch   # use the -f switch, if it's given, or use a default configuration filename   my $config_file = $options{f} || 'first.conf';   print "Configuration file is $config_file\n";   # check for the -l switch   if ($options{l})   {   system('/bin/ls -l');   }   else   {   system('/bin/ls');   }   # print out the help and exit   sub usage   {   print   first.pl [-l] [-h] [-f FILENAME]   Lists the files in the current directory, using either /bin/ls or   /bin/ls -l.   The -f switch selects a different configuration file.   The -h switch prints this help.   EOHIPPUS   exit;   }   简单的事件循环   当命令行参数不够用时,下一步是编写一个事件循环。在这种方案中,仍然可接受命令行参数,并且有时只使用命令行参数就足够了。然而,事件循环支持用户在不输入任何参数的情形下调用程序,以及看到提示符。在此提示符下,通常可使用 help 命令,这将打印出更详细的帮助。有时,这个 help 甚至可以是一个单独的输入提示符,有一个完整的软件子系统来专门负责它。   带有命令行开关的事件循环   #!/usr/bin/perl -w   use strict; # always use strict, it's a good habit   use Getopt::Std; # see "perldoc Getopt::Std"   my %options;   getopts('f:hl', \%options); # read the options with getopts   # uncomment the following two lines to see what the options hash contains   #use Data::Dumper;   #print Dumper \%options;   $options{h} && usage(1); # the -h switch, with exit option   # use the -f switch, if it's given, or use a default configuration filename   my $config_file = $options{f} || 'first.conf';   print "Configuration file is $config_file\n";   # check for the -l switch   if ($options{l})   {   system('/bin/ls -l');   }   else   {   my $input; # a variable to hold user input   do   {   print "Type 'help' for help, or 'quit' to quit\n- ";   $input = ;   print "You entered $input\n"; # let the user know what we got   # note that 'listlong' matches /list/, so listlong has to come first   # also, the i switch is used so upper/lower case makes no difference   if ($input =~ /listlong/i)   {   system('/bin/ls -l');   }   elsif ($input =~ /list/i)   {   system('/bin/ls');   }   elsif ($input =~ /help/i)   {   usage();   }   elsif ($input =~ /quit/i)   {   exit;   }   }   while (1); # only 'quit' or ^C can exit the loop   }   exit; # implicit exit here anyway   # print out the help and exit   sub usage   {   my $exit = shift @_ || 0; # don't exit unless explicitly told so   print   first.pl [-l] [-h] [-f FILENAME]   The -l switch lists the files in the current directory, using /bin/ls -l.   The -f switch selects a different configuration file.   The -h   switch prints this help.   Without the -l or -h arguments, will show   a command prompt.   Commands you can use at the prompt:   list:   list the files in the current directory   listlong:   list the files in the current directory in long format   help:   print out this help   quit:   quit the program   EOHIPPUS   exit if $exit;   }   这里,通常会有三种选择:   由于可能会有多个开关组合,所以程序的 UI 会复杂到不可忍受的程度。   UI 将发展为 GUI。   从头重新开发 UI,使其至少带有一些解析功能。   第一种选择太可怕,难以去想象。这里不讨论第二种选择,但它的确在向后兼容性和灵活性方面提出了有趣的挑战。第三种选择是本文下面要讨论的内容。   Parse::RecDescent 的快速教程   Parse::RecDescent 是一个用于解析文本的模块。通过几个简单构造就可以用它完成几乎所有的解析任务。更高级的文法构造可能会让人望而生畏,不过在大多数用途中不需要这么高级的文法。   Parse::RecDescent 是一个面向对象的模块。它围绕着文法创建解析器对象。文法(grammar)是一组以文本形式表示的规则。下面这个示例是一条匹配单词的规则:   word 规则   word: /\w+/   这条规则一次或多次匹配字符(\w)。跟在冒号后的部分称为产品(production)。一条规则必须包含至少一个产品。一个产品可以包含其它规则或直接要匹配的内容。下面这个示例是一条规则,它可以匹配一个单词、另一条规则(non-word)或一个错误(如果其它两个都失败):   另一些产品   token: word | non-word |   word: /\w+/   non-word: /\W+/   每个产品也可以包含一个用花括号括起的操作:   产品中的操作   print: /print/i { print_function(); }   如果操作是产品中的最后一项,则操作的返回码决定产品是否成功。该操作是一种空产品,它将总是会匹配,除非它返回 0。   可以用 (s) 修饰符指定多个标记(token):   一个产品中带一个或多个标记   word: letter(s)   letter: /\w/   也可以将 (?)(0 或 1)和 (s?)(0 到 N)修饰符作为可选关键字来使用。   可以通过 $item[position] 或 $item{name} 机制来访问产品中的任何内容。请注意,在第二种情形中,两个单词的名称是相同的,所以必须使用位置查找。在第三种情形中,单词数组以数组引用的形式存储在 $item{word} 中。如果在产品中使用可选项,则数组定位方案将肯定无法很好地工作。一般来说,无论如何都应避免使用这种方案,因为通过名称查找的方式总是更方便更简单:   使用 %item 和 @item 变量   print: /print/i word { print_function($item{word}); }   print2: /print2/i word word { print_function($item[1], $item[2]); }   print3: /print3/i w   
󰈣󰈤
 
 
 
>>返回首页<<
 
 热帖排行
 
 
王朝网络微信公众号
微信扫码关注本站公众号wangchaonetcn
 
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
©2005- 王朝网络 版权所有