Perl编程练习与解答汇总

内容分享2天前发布
0 0 0

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

以下是修改后的代码:


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 代码:


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;

来关闭目录句柄,避免资源泄漏。

© 版权声明

相关文章

暂无评论

none
暂无评论...