Perl编程练习题及代码示例

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、编写一个Perl程序,使其输出“Hello, world”并运行,以此测试Perl环境是否正常工作。

可以使用以下代码实现:


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)。


#!/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,最后输出圆的半径和对应的周长。例如,如果用户输入的半径为 12.5,程序应计算并输出该半径对应的圆的周长。

以下是实现该功能的代码:


#!/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.
";

该代码提示用户输入半径,将输入存储在变量

$radius

中,计算圆的周长,并处理半径为负数的情况,最后输出结果。

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 "请输入第一个数字:";
$num1 = <STDIN>;
chomp($num1);
print "请输入第二个数字:";
$num2 = <STDIN>;
chomp($num2);
$result = $num1 * $num2;
print "两数相乘的结果是:$result
";

7、编写一个程序,提示用户输入一个字符串和一个数字(分别在不同的输入行),然后将该字符串按照数字所指定的次数,逐行打印输出。(提示:使用乘法运算符)

以下是使用 Python 实现的代码示例:


string = input("请输入一个字符串:")
number = int(input("请输入一个数字:"))
print((string + "
") * number)

上述代码首先通过

input

函数获取用户输入的字符串和数字,然后使用乘法运算符将字符串重复指定次数,并逐行打印输出。

8、编写一个名为 total 的子例程,它返回一个数字列表的总和。该子例程不应执行任何输入输出操作,应仅处理其参数并将一个值返回给调用者。请在一个示例程序中测试这个子例程,示例程序中第一组数字 1、3、5、7、9 的总和应该是 25,并且程序还应允许用户输入一些数字,计算这些输入数字的总和并输出结果。

以下是实现

total

子例程的代码:


sub total {
    my $sum;
    foreach (@_) {
        $sum += $_;
    }
    $sum;
}

为避免可能返回未定义值,可将

$sum

初始化为

0


sub total {
    my $sum = 0;
    foreach (@_) {
        $sum += $_;
    }
    return $sum;
}

9、编写一个程序来计算 1 到 1000 的数字之和。

由于不清楚上一题子程序具体内容无法直接使用,这里给出 Python 示例代码:


result = sum(range(1, 1001))
print(f'The numbers from 1 to 1000 add up to {result}.')

10、编写一个名为 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)
";

上述代码中,

total

子程序用于计算列表中所有数字的总和,

average

子程序调用

total

子程序计算平均值,

above_average

子程序调用

average

子程序得到平均值,并筛选出高于平均值的数字。

11、编写一个名为 greet 的子程序,当你传入人名时,它会欢迎这个人,并告知他们上一个被问候的人的名字。例如执行 greet( “Fred” ); greet( “Barney” ); 这两条语句时,应该输出:Hi Fred! You are the first one here! Hi Barney! Fred is also here!

以下是实现该功能的代码:


use 5.010;
greet( 'Fred' );
greet( 'Barney' );
sub greet {
    state $last_name;
    my $name = shift;
    print "Hi $name! ";
    if( defined $last_name ) {
        print "$last_name is also here!
";
    } else {
        print "You are the first one here!
";
    }
    $last_name = $name;
}

12、修改之前的程序,让它告知每个新到来的人之前已经问候过的所有人的名字。执行以下语句序列: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;
}

13、编写一个程序,要求用户在不同行输入一系列字符串,将每个字符串打印在右对齐的20字符列中。为确保输出在正确的列中,同时打印一行数字作为“标尺线”(这只是一个调试辅助工具)。要确保不会误使用19字符列!例如,输入hello、good – bye,输出应类似如下:123456789012345678901234567890123456789012345678901234567890

hello

good – bye

以下是实现该功能的代码示例:


print "Enter some lines, then press Ctrl - D:"; # 提示用户输入
chomp(my @lines = <STDIN>); # 读取输入并去除换行符
print "1234567890" x 6, "
"; # 打印标尺线
foreach (@lines) {
    printf "%20s
", $_; # 右对齐打印每个字符串
}

在上述代码中,首先提示用户输入一系列字符串,然后使用

<STDIN>

读取输入并存储在

@lines

数组中,同时使用

chomp

去除换行符。接着打印标尺线,最后使用

foreach

循环遍历数组,使用

printf

函数将每个字符串右对齐打印在20字符列中。

14、编写一个程序,让用户可以选择列宽,用户分行输入列宽、字符串,例如输入 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
", $_;
}

该代码先让用户输入列宽,接着输入一些行,然后根据输入的列宽生成长度合适的标尺线,最后将每行字符串按指定列宽右对齐输出。此外,也可以使用


printf "%*s
", $width, $_;

这种方式来输出,避免使用字符串插值时可能出现的问题。

15、编写一个程序,要求用户输入一个名字,并报告对应的姓氏。对于输入’fred’,输出’flintstone’;输入’barney’,输出’rubble’;输入’wilma’,输出’flintstone’。若输入的名字不在上述范围内,提示未找到对应的姓氏。

以下是一个用 Perl 语言实现的示例程序:


use strict;
use warnings;

# 定义名字和姓氏的映射
my %name_map = ( 
    'fred'   => 'flintstone', 
    'barney' => 'rubble', 
    'wilma'  => 'flintstone'
);

# 提示用户输入名字
print "请输入一个名字: ";
my $given_name = <STDIN>;
chomp $given_name;

# 将名字转换为小写,以便不区分大小写
$given_name = lc $given_name;

# 检查输入的名字是否在映射中
if (exists $name_map{$given_name}) { 
    print "对应的姓氏是: $name_map{$given_name}
";
} else { 
    print "未找到对应的姓氏。
";
}

这个程序首先定义了一个哈希表

%name_map

,用于存储名字和姓氏的映射关系。然后,它提示用户输入一个名字,并将输入的名字转换为小写,以便不区分大小写。最后,它检查输入的名字是否在哈希表中,如果存在,则输出对应的姓氏;否则,输出未找到对应的姓氏的提示信息。

16、编写一个程序,逐行读取一系列单词,直到输入结束,然后打印出每个单词出现的次数摘要。如果输入的单词是 fred、barney、fred、dino、wilma、fred(每行一个单词),输出应显示 fred 出现了 3 次。额外加分项:按代码点顺序对输出的摘要单词进行排序。

以下是实现该功能的 Perl 代码:


my %count;
while (<STDIN>) {
    chomp;
    $count{$_}++;
}
foreach my $word (sort keys %count) {
    print "$word was seen $count{$word} times.
";
}

这段代码首先使用

while

循环逐行读取标准输入,使用

chomp

去除每行末尾的换行符,然后使用哈希表

%count

统计每个单词的出现次数。最后,使用

sort

函数对哈希表的键进行排序,并打印出每个单词及其出现次数。

17、编写一个程序,打印输入中提到“fred”的每一行(对于其他行不做处理)。如果输入字符串是“Fred”、“frederick”或“Alfred”,程序是否会匹配?创建一个包含几行提及“fred flintstone”及其朋友的小文本文件,然后将该文件作为此程序的输入,程序会有怎样的输出?

要编写这样的程序,可将模式改为

/[fF]red/

。正则表达式区分大小写,原模式不匹配“Fred”,但修改后的模式可以。“frederick”和“Alfred”能匹配,因为它们都包含“fred”。

以下是示例代码:


while (<>) {
    if (/[fF]red/) {
        print;
    }
}

将包含“fred flintstone”及其朋友的文本内容保存为文件,运行此程序并将该文件作为输入,程序会打印出包含“fred”的行。

18、修改之前的程序,使其也能匹配“Fred”。如果输入字符串是“Fred”、“frederick”或“Alfred”,现在能匹配上吗?(假设需要通过正则表达式来实现匹配,并将这些名字添加到文本文件中进行匹配测试)

正则表达式区分大小写,原本不匹配“Fred”。

修改模式为

/[fF]red/

可匹配。

“frederick”和“Alfred”能匹配,因为它们包含“fred”。

若要只匹配整个单词,后续会介绍相关功能。

19、编写一个程序,打印出每行中包含一个首字母大写但并非全大写的单词的行。该程序能匹配 Fred,但不匹配 fred 或 FRED 吗?

可以将之前程序中使用的模式更改为

/[A-Z][a-z]+/

来实现此功能。该模式能匹配

Fred

,但不匹配

fred


FRED

,因为它要求单词首字母为大写,后续字母为小写。

20、附加题:编写一个程序,打印出任何同时提及了“wilma”和“fred”的输入行。

可使用正则表达式和编程语言来实现该程序。以 Python 为例,代码如下:


while True:
    try:
        line = input()
        if 'wilma' in line.lower() and 'fred' in line.lower():
            print(line)
    except EOFError:
        break

上述代码通过循环不断读取输入行,将每行转换为小写后检查是否同时包含“wilma”和“fred”,若包含则打印该行。

21、使用模式测试程序,创建一个模式,用于匹配任何以字母 a 结尾的单词(这里的单词是指符合 w 定义的单词)。该模式是否能匹配 wilma 但不匹配 barney?是否能匹配字符串 ‘Mrs. Wilma Flintstone’ 中的单词?是否能匹配字符串 ‘wilma&fred’ 中的单词?

可使用模式

/w*a/

来匹配以字母

a

结尾的单词。此模式能匹配

wilma

但不匹配

barney

,能匹配

'Mrs. Wilma Flintstone'

中的

Wilma

,也能匹配

'wilma&fred'

中的

wilma

22、创建一个模式,该模式能匹配当前 $what 变量中内容的三个连续副本。也就是说,如果 $what 是 fred,你的模式应匹配 fredfredfred;如果 $what 是 fred|barney,你的模式应匹配 fredfredbarney、barneyfredfred、barneybarneybarney 等多种变体。(提示:你应该在模式测试程序的开头使用类似 my $what = ‘fred|barney’; 的语句来设置 $what。)

可使用模式

/$what{3}/

来匹配

$what

的三个连续副本。在 Perl 代码中,示例如下:


my $what = 'fred|barney';
my $string = '待匹配的具体字符串';
if ($string =~ /($what){3}/) {
    print "匹配成功
";
}

其中

$string

是待匹配的字符串。

23、编写一个程序,对文本文件进行修改复制。在复制文件中,每个字符串 Fred(不区分大小写)应替换为 Larry。(例如,Manfred Mann 应变为 ManLarry Mann。)输入文件名应在命令行中给出(不要询问用户),输出文件名应为对应的以 .out 结尾的文件名。

以下是实现该功能的完整 Perl 程序代码:


use strict;
use warnings;

# 检查命令行参数
if (@ARGV != 1) {
    die "Usage: perl script.pl <input_file>
";
}

my $in = $ARGV[0];
my $out = $in; 
$out =~ s/.[^.]*$/.out/ if $out =~ /./; # 如果有扩展名,替换为 .out
$out .= ".out" if $out !~ /./; # 如果没有扩展名,追加 .out

# 打开输入文件
open my $in_fh, '<', $in or die "Can't open '$in': $!";
# 打开输出文件
open my $out_fh, '>', $out or die "Can't write '$out': $!";

# 逐行读取输入文件,进行替换并写入输出文件
while (my $line = <$in_fh>) {
    $line =~ s/Fred/Larry/gi; # 不区分大小写的全局替换
    print $out_fh $line;
}

# 关闭文件句柄
close $in_fh;
close $out_fh;

该程序首先检查命令行参数是否正确,获取输入文件名。然后将输入文件名处理为对应的以

.out

结尾的输出文件名。接着打开输入和输出文件句柄,逐行读取输入文件,使用

s/Fred/Larry/gi

进行不区分大小写的全局替换,将

Fred

替换为

Larry

,最后将处理后的行写入输出文件。处理完成后关闭文件句柄。

24、编写一个程序,将版权声明行(如 ## Copyright (C) 20XX by Yours Truly)添加到指定的练习答案文件中,该声明行应紧跟在“shebang”行之后。你需要对文件进行“原地”编辑,并保留备份。假设程序调用时,待编辑的文件名已经在命令行中给出。

以下是实现该功能的 Perl 程序:


@ARGV = @ARGV;
$^I = ".bak"; # 制作备份

while (<>) {
    if (/^#!/) { # 是否为 shebang 行?
        $_ .= "## Copyright (C) 20XX by Yours Truly
";
    }
    print;
}

上述代码首先通过

@ARGV

获取命令行传入的文件名,然后设置文件备份后缀为

.bak

。接着使用

while (<>)

逐行读取文件内容,当遇到以

#!

开头的 shebang 行时,在该行后面添加版权声明行,最后将处理后的内容输出,实现原地编辑文件并保留备份。

25、修改一个程序,使其不编辑已经包含版权声明行的文件。提示:正在读取的文件名存储在 $ARGV 中。

以下是实现该功能的代码:

首先,为避免重复添加版权信息,对文件进行两次遍历。第一次,创建一个哈希

%do_these

,键为文件名,值设为

1


my %do_these;
foreach (@ARGV) {
    $do_these{$_} = 1;
}

接着,检查文件,若文件已包含版权声明,则从待处理列表中移除该文件。当前文件名存储在

$ARGV

中,可作为哈希键:


while (<>) {
    if (/A## Copyright/) {
        delete $do_these{$ARGV};
    }
}

最后,重新设置

@ARGV

为缩减后的文件名列表,再执行添加版权信息的操作:


@ARGV = sort keys %do_these;
$^I = ".bak"; # 制作备份

while (<>) {
    if (/A#!/) { # 是否为 shebang 行
        $_ .= "## Copyright (c) 20XX by Yours Truly
";
    }
    print;
}
© 版权声明

相关文章

暂无评论

none
暂无评论...