| 導購 | 订阅 | 在线投稿
分享
 
 
 

功能豐富的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- 王朝網路 版權所有