table {
border-collapse: collapse;
width: 100%;
margin-bottom: 1rem;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
pre {
background-color: #f8f8f8;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
}
1、当练习文本开头出现方括号内的数字 2,即 [2] 时,这个数字 2 意味着什么?
这个数字是对完成该特定练习所需分钟数的大致估计。
不过这只是粗略估算,实际完成时间可能是该数字的一半,也可能是两倍。
2、编写一个 Perl 程序,使其输出“Hello, world”并运行该程序。
若使用 Perl 5.10 之前版本,代码为:
print "Hello, world!
";
若使用 Perl 5.10 或更高版本,代码为:
use 5.010;
say "Hello, world!";
如果想在命令行运行而不创建文件,可使用
-e
开关指定程序,如:
$ perl -e 'print "Hello, World
"'
也可使用
-l
开关自动添加换行符,如:
$ perl -le 'print "Hello, World"'
3、编写一个程序来计算半径为 12.5 的圆的周长。圆的周长是 2π乘以半径(约为 2 乘以 3.141592654)。你得到的答案应该约为 78.5。
#!/usr/bin/perl -w
$pi = 3.141592654;
$circ = 2 * $pi * 12.5;
print "The circumference of a circle of radius 12.5 is $circ.
";
4、编写一个程序,提示用户输入圆的半径,根据输入的半径计算圆的周长。若半径为负数,则周长按 0 处理,最后输出圆的半径和对应的周长。
#!/usr/bin/perl -w
$pi = 3.141592654;
print "What is the radius? ";
chomp($radius = <STDIN>);
$circ = 2 * $pi * $radius;
if ($radius < 0) {
$circ = 0;
}
print "The circumference of a circle of radius $radius is $circ.
";
5、编写一个 Perl 程序,让用户输入一个圆的半径,若用户输入的数字小于零,程序报告的圆周长将为零,而不是负数。
#!/usr/bin/perl -w
$pi = 3.141592654;
print "What is the radius? ";
chomp($radius = <STDIN>);
$circ = 2 * $pi * $radius;
if ($radius < 0) {
$circ = 0;
}
print "The circumference of a circle of radius $radius is $circ.
";
6、编写一个程序,提示用户输入两个数字(分别在两行输入),并输出这两个数字相乘的结果。
以下是一个满足需求的Perl程序示例:
print "Enter the first number: ";
chomp($num1 = <STDIN>);
print "Enter the second number: ";
chomp($num2 = <STDIN>);
$result = $num1 * $num2;
print "The product of the two numbers is: $result
";
7、编写一个程序,提示用户输入一个字符串和一个数字(分别在不同的输入行),并将该字符串按照数字指定的次数打印在不同的行上。(提示:使用 x 运算符)。如果用户输入“fred”和“3”,输出应该是三行,每行都显示“fred”。如果用户输入“fred”和“299792”,可能会有大量输出。
以下是实现该功能的程序:
print "Enter a string: ";
$str = <STDIN>;
print "Enter a number of times: ";
chomp($num = <STDIN>);
$result = $str x $num;
print "The result is:
$result";
8、编写一个名为 total 的子例程,它返回一个数字列表的总和。该子例程不应执行任何输入/输出操作,只需处理其参数并将一个值返回给调用者。编写一个示例程序来测试这个子例程,程序中第一组数字为 1、3、5、7、9,其总和应该是 25。程序还应允许用户输入一些数字,计算并输出这些数字的总和。
以下是实现该功能的代码:
sub total {
my $sum;
foreach (@_) {
$sum += $_;
}
$sum;
}
为避免空参数列表时返回
undef
,可将代码改为:
sub total {
my $sum = 0;
foreach (@_) {
$sum += $_;
}
$sum;
}
9、编写一个名为 above_average 的子程序,该子程序接受一个数字列表,并返回其中高于平均值(均值)的数字。(提示:编写另一个子程序,通过将总和除以项数来计算平均值。)在以下测试程序中测试你的子程序。my @fred = above_average(1..10); print “@fred is @fred
”; print “(Should be 6 7 8 9 10)
“; my @barney = above_average(100, 1..10); print “@barney is @barney
“; print “(Should be just 100)
“;
可以编写如下代码实现:
首先编写计算总和的子程序
total
,再编写计算平均值的子程序
average
,最后编写
above_average
子程序。代码如下:
sub total {
my $sum = 0;
foreach my $num (@_) {
$sum += $num;
}
return $sum;
}
sub average {
my @list = @_;
if (!@list) {
return undef;
}
my $sum = total(@list);
my $count = scalar @list;
return $sum / $count;
}
sub above_average {
my @list = @_;
my $average = average(@list);
my @result;
foreach my $element (@list) {
if ($element > $average) {
push @result, $element;
}
}
return @result;
}
my @fred = above_average(1..10);
print "@fred is @fred
";
print "(Should be 6 7 8 9 10)
";
my @barney = above_average(100, 1..10);
print "@barney is @barney
";
print "(Should be just 100)
";
10、编写一个名为 greet 的子程序,当你传入人名时,它会欢迎这个人,并告知他们上一个被问候的人的名字。例如,执行 greet( “Fred” ); greet( “Barney” ); 这两条语句时,应该输出:Hi Fred! You are the first one here! Hi Barney! Fred is also here!
以下是实现该功能的 Perl 代码:
use 5.010;
greet( 'Fred' );
greet( 'Barney' );
sub greet {
state $last_name;
my $name = shift;
print "Hi $name! ";
if ($last_name) {
print "$last_name is also here!
";
} else {
print "You are the first one here!
";
}
$last_name = $name;
}
11、修改程序,让它在每次执行
greet
函数时,告诉新到来的人之前已经问候过的所有人的名字。执行以下语句序列:
greet( "Fred" ); greet( "Barney" ); greet( "Wilma" ); greet( "Betty" );
这些语句应该输出:
Hi Fred! You are the first one here! Hi Barney! I've seen: Fred Hi Wilma! I've seen: Fred Barney Hi Betty! I've seen: Fred Barney Wilma
greet
greet( "Fred" ); greet( "Barney" ); greet( "Wilma" ); greet( "Betty" );
Hi Fred! You are the first one here! Hi Barney! I've seen: Fred Hi Wilma! I've seen: Fred Barney Hi Betty! I've seen: Fred Barney Wilma
以下是修改后的代码:
use 5.010;
greet( 'Fred' );
greet( 'Barney' );
greet( 'Wilma' );
greet( 'Betty' );
sub greet {
state @names;
my $name = shift;
print "Hi $name! ";
if( @names ) {
print "I've seen: @names
";
} else {
print "You are the first one here!
";
}
push @names, $name;
}
12、编写一个程序,要求用户在不同的行输入一系列字符串,将每个字符串右对齐打印在一个20字符宽的列中。为确保输出列格式正确,同时打印一行数字作为“标尺线”(这只是一个调试辅助手段)。要确保不会误使用19字符宽的列!例如,输入hello和good – bye,输出应类似如下形式:123456789012345678901234567890123456789012345678901234567890
hello
good – bye
以下是实现该功能的代码示例:
print "Enter some lines, then press Ctrl - D:
"; # or Ctrl - Z
chomp(my @lines = <STDIN>);
print "1234567890" x 6, "
";
foreach (@lines) {
printf "%20s
", $_;
}
13、修改程序,让用户可以选择列宽,这样输入30、hello、good – bye(分行输入)会将字符串显示在第30列。额外加分项:当所选宽度较大时,让标尺线更长。
print "What column width would you like? ";
chomp(my $width = <STDIN>);
print "Enter some lines, then press Ctrl - D:
";
# or Ctrl - Z
chomp(my @lines = <STDIN>);
print "1234567890" x (($width + 9)/10), "
";
# ruler line as needed
foreach (@lines) {
printf "%${width}s
", $_;
}
14、编写一个程序,要求用户输入一个名字,并输出对应的姓氏。当输入为 ‘fred’ 时,输出 ‘flintstone’;当输入为 ‘barney’ 时,输出 ‘rubble’;当输入为 ‘wilma’ 时,输出 ‘flintstone’。若输入的名字不在给定范围内,则输出未找到对应的姓氏的信息。
以下是一个使用 Perl 语言实现的示例代码:
use strict;
use warnings;
# 定义名字和姓氏的映射
my %name_family = (
'fred' => 'flintstone',
'barney' => 'rubble',
'wilma' => 'flintstone'
);
# 提示用户输入名字
print "请输入一个名字: ";
my $given_name = <STDIN>;
chomp $given_name;
# 将输入转换为小写,以实现不区分大小写的匹配
$given_name = lc $given_name;
# 检查输入的名字是否存在于映射中
if (exists $name_family{$given_name}) {
print "对应的姓氏是: $name_family{$given_name}
";
} else {
print "未找到对应的姓氏。
";
}
这个程序首先定义了一个哈希表
%name_family
,其中包含了名字和姓氏的映射。然后,它提示用户输入一个名字,并将输入的名字转换为小写,以实现不区分大小写的匹配。最后,它检查输入的名字是否存在于哈希表中,如果存在,则输出对应的姓氏;否则,输出未找到对应的姓氏的信息。
15、编写一个程序,打印输入中提到“fred”的每一行(对于其他行不做处理)。如果输入字符串是“Fred”、“frederick”或“Alfred”,程序是否会匹配?创建一个包含几行提及“fred flintstone”及其朋友的小文本文件,然后将该文件作为此程序的输入。
可将第一个练习答案中的模式改为
/[fF]red/
来实现该程序。由于原正则表达式区分大小写,原模式不匹配“Fred”,但修改后的模式可以匹配“Fred”、“frederick”和“Alfred”,因为它们都包含“fred”。
16、修改之前的程序,使其也能匹配Fred。如果输入字符串是Fred、frederick或Alfred,修改后的程序能匹配这些字符串吗?(将这些名字添加到文本文件中)
正则表达式匹配处理
正则表达式区分大小写,最初不能匹配
Fred
。
修改模式为以下任意一种形式可实现匹配:
/[fF]red/
/(f|F)red/
/fred|Fred/
其中,使用字符类
/[fF]red/
的方式更为高效。
修改后能匹配
frederick
和
Alfred
,因为它们都包含字符串
fred
;而后续将介绍如何实现匹配全词以排除
frederick
和
Alfred
的功能。
17、编写一个程序,打印出每行中包含一个首字母大写但并非全大写的单词的行。该程序是否能匹配“Fred”,而不匹配“fred”和“FRED”?
一种实现方式是将使用的模式改为
/[A-Z][a-z]+/
。该模式能匹配“Fred”,但不匹配“fred”和“FRED”。
18、创建一个模式,用于匹配当前 $what 变量中内容的三个连续副本。也就是说,如果 $what 的值是 fred,你的模式应该匹配 fredfredfred;如果 $what 的值是 fred|barney,你的模式应该匹配 fredfredbarney、barneyfredfred、barneybarneybarney 等多种组合。(提示:你应该在模式测试程序的顶部使用类似 my $what = ‘fred|barney’; 的语句来设置 $what。)
在 Perl 中可以使用如下代码实现:
my $what = 'fred|barney';
my $pattern = qr/($what){3}/;
19、编写一个程序,对文本文件进行修改复制。在复制文件中,每个字符串 Fred(不区分大小写)应替换为 Larry。(例如,Manfred Mann 应变为 ManLarry Mann。)输入文件名应在命令行中给出(不要询问用户),输出文件名应为对应的以 .out 结尾的文件名。
if (! open $in_fh, '<', $in ) { die "Can't open '$in': $!";}
if (! open $out_fh, '>', $out ) { die "Can't write '$out': $!";}
while (<$in_fh>) {
s/Fred/Larry/gi;
print $out_fh $_;
}
该程序首先指定唯一的命令行参数,若未获取到则报错。然后将其复制到
$out
并进行替换,将文件扩展名(如果有)改为
.out
。打开文件句柄
IN
和
OUT
后,开始实际的程序操作。如果未同时使用
/g
和
/i
选项,会被扣除一半分数,因为每个
fred
和
Fred
都应被替换。
20、编写一个程序,将类似“## Copyright (C) 20XX by Yours Truly”这样的版权声明行添加到指定的练习答案文件中,该版权声明行应紧跟在“shebang”行之后。你应该对文件进行“原地”编辑,并保留备份。假设程序在调用时,需要编辑的文件名已经在命令行中给出。
以下是实现该功能的Perl代码:
@ARGV = @ARGV; # 从命令行获取文件名
$^I = ".bak"; # 开启备份功能,备份文件后缀为.bak
while (<>) { # 逐行读取文件
if (/^#!/) { # 检查是否为shebang行
$_ .= "## Copyright (C) 20XX by Yours Truly
"; # 在shebang行后添加版权声明
}
print; # 输出当前行
}
上述代码首先从命令行获取需要处理的文件名,然后使用
$^I
变量开启文件的原地编辑功能,并指定备份文件的后缀为
.bak
。接着,使用
while (<>)
循环逐行读取文件内容,当遇到以
#!
开头的shebang行时,在该行后面添加版权声明行。最后,将处理后的内容输出到文件中。
21、编写一个程序,该程序会不断要求用户猜测一个1到100之间的秘密数字,直到用户猜对为止。程序应使用神奇公式 int(1 + rand 100) 随机选取数字。当用户猜错时,程序应回应“Too high”或“Too low”。如果用户输入“quit”或“exit”,或者输入空行,程序应退出。当然,如果用户猜对了,程序也应退出!
以下是一个满足需求的Perl程序示例:
use 5.010;
my $secret = int(1 + rand 100);
while (1) {
print "Please enter a guess from 1 to 100 (or 'quit'/'exit' to quit): ";
chomp(my $guess = <STDIN>);
if ($guess eq 'quit' || $guess eq 'exit' || $guess eq '') {
last;
}
if ($guess =~ /^d+$/ ) {
if ($guess > $secret) {
print "Too high
";
} elsif ($guess < $secret) {
print "Too low
";
} else {
print "You guessed it right!
";
last;
}
} else {
print "Please enter a valid number or 'quit'/'exit' to quit.
";
}
}
这个程序首先使用
int(1 + rand 100)
生成一个1到100之间的随机数作为秘密数字。然后进入一个无限循环,不断提示用户输入猜测的数字。如果用户输入“quit”、“exit”或空行,程序会退出循环。如果用户输入的是有效的数字,程序会根据猜测结果给出“Too high”或“Too low”的提示,猜对则输出正确信息并退出循环。如果输入不是有效的数字,程序会提示用户输入有效的数字。
22、编写一个 Perl 程序,程序会随机选择一个 1 到 100 之间的秘密数字。要求在运行过程中可以打印额外的调试信息,例如它选择的秘密数字。要能够关闭此功能,并且在关闭时程序不会发出警告。如果你使用的是 Perl 5.10 或更高版本,请使用 // 运算符。否则,请使用条件运算符。
使用 Perl 5.10 或更高版本:
use 5.010;
my $Debug = $ENV{DEBUG} // 1;
my $secret = int(1 + rand 100);
print "Don't tell anyone, but the secret number is $secret.
" if $Debug;
不使用 Perl 5.10 新特性:
my $Debug = defined $ENV{DEBUG} ? $ENV{DEBUG} : 1;
my $secret = int(1 + rand 100);
print "Don't tell anyone, but the secret number is $secret.
" if $Debug;
23、编写一个使用 DateTime 模块的 Perl 程序,用于计算当前时间与在命令行输入的年、月、日所表示的日期之间的时间间隔,例如:运行
perl duration.pl 1960 9 30
会输出
50 years, 8 months, and 20 days
。
perl duration.pl 1960 9 30
50 years, 8 months, and 20 days
以下是实现该功能的 Perl 代码:
use DateTime;
my $now = DateTime->now;
my $then = DateTime->new(
year => $ARGV[0],
month => $ARGV[1],
day => $ARGV[2],
);
my $duration = $now - $then;
my @units = $duration->in_units( qw(years months days) );
printf "%d years, %d months, and %d days
", @units;
上述代码会计算当前日期和命令行输入的日期之间的时间间隔,并以指定格式输出。
24、编写一个程序,使用堆叠文件测试操作符列出命令行中指定的所有可读、可写且归当前用户所有的文件。
使用 Perl 5.10 及以上版本,代码示例如下:
use 5.010;
die "No files specified!
" unless @ARGV;
foreach my $file ( @ARGV ) {
print "$file is readable, writable and owned by you
" if( -w -r -o $file );
}
上述代码首先检查命令行是否指定了文件,若未指定则终止程序。然后遍历命令行指定的每个文件,使用堆叠文件测试操作符
-w -r -o
检查文件是否可读、可写且归当前用户所有,若满足条件则将文件名输出。
25、编写一个程序,向用户询问一个目录名,然后切换到该目录。如果用户输入的行只有空白字符,则默认切换到他或她的主目录。切换后,按字母顺序列出普通目录内容(不以点开头的项目)。(提示:使用目录句柄还是通配符更容易做到这一点?)如果目录切换不成功,只需提醒用户,但不要尝试显示内容。
以下是实现该功能的 Perl 代码:
print 'Which directory? (Default is your home directory) ';
chomp(my $dir = <STDIN>);
if ($dir =~ /As*/) {
chdir or die "Can't chdir to your home directory: $!";
} else {
chdir $dir or die "Can't chdir to '$dir': $!";
}
opendir DOT, "." or die "Can't opendir dot: $!";
foreach (sort readdir DOT) {
next if /A./;
print "$_
";
}
closedir DOT;
此代码首先提示用户输入目录名,若输入为空则切换到主目录。成功切换目录后,使用
opendir
打开当前目录,通过
readdir
读取目录内容,用
sort
排序,最后过滤掉以点开头的文件并输出结果。若目录切换失败,会输出相应错误信息。同时添加了
closedir DOT;
来关闭目录句柄,避免资源泄漏。