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;
}

24、创建一个名为 Does::ToHash 的角色,该角色将对象的属性转换为哈希。扩展 User 类以使用此角色并进行测试。

创建

Does::ToHash

角色的代码如下:


package Does::ToHash;
use Moose::Role;
sub to_hash {
    my $self = shift;
    my @attributes = map { $_->name } $self->meta->get_all_attributes;
    my %hash;
    foreach my $attribute (@attributes) {
        my $value = $self->$attribute;
        next if ref $value;
        $hash{$attribute} = $value;
    }
    return \%hash;
}
1;

扩展

User

类以使用

Does::ToHash

角色的代码如下:


package User;
use Moose;
with 'Does::ToHash';
use Digest::MD5 'md5_hex';
use namespace::autoclean;

has username => ( is => 'ro', isa => 'Str', required => 1 );
has password => ( is => 'ro', isa => 'Str', writer => '_set_password',);

sub BUILD {
    my $self = shift;
    $self->_set_password(md5_hex($self->password));
}

sub password_eq {
    my ( $self, $password ) = @_;
    $password = md5_hex($password);
    return $password eq $self->password;
}

__PACKAGE__->meta->make_immutable;
1;

测试代码如下:


use Data::Dumper;
my $user = User->new( username => 'Ovid', password => 'Corinna',);
print Dumper($user->to_hash);

25、编写一个名为 unique 的子例程,该子例程返回数组中的唯一元素,且元素顺序与它们在原始列表中出现的顺序相同,并对其进行测试。

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


use Test::Most;
sub unique {
    my @array = @_;
    my %seen;
    my @unique;
    foreach my $element (@array) {
        push @unique, $element unless $seen{$element}++;
    }
    return @unique;
}
my @have = unique( 2, 3, 5, 4, 3, 5, 7 );
my @want = ( 2, 3, 5, 4, 7 );
is_deeply @have, @want, 'unique() should return unique elements in order';
done_testing;

26、修改 unique 子例程的测试,通过对实际数组和预期数组进行排序,以确保唯一元素是有序的。

可将测试代码修改为:


is_deeply [ sort @have ], [ sort @want ], 'unique() should return unique() elements in order';

这样就对

@have


@want

数组都进行了排序,从而保证比较时能确保唯一元素是有序的。

27、编写一个子例程 reciprocal 来计算一个数的倒数,处理非数字输入和除以零的情况,并对其进行测试。

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


use Test::Most;
use Carp 'croak';
use Scalar::Util 'looks_like_number';

sub reciprocal {
    my $number = shift;
    unless ( looks_like_number($number) ) {
        croak("Argument to reciprocal() must be a number");
    }
    unless ($number) {
        croak("Illegal division by zero");
    }
    return 1 / $number;
}

throws_ok { reciprocal([]) } qr/Argument to reciprocal() must be a number/, 'Passing non - numbers to reciprocal() should fail';
# diag reciprocal([]); 这行代码会因为前面的异常抛出而无法正常执行,应注释或删除
# 这里可以添加更多测试用例,例如测试正常数字输入
is(reciprocal(2), 0.5, 'Reciprocal of 2 should be 0.5');
done_testing;

上述代码定义了

reciprocal

子例程,用于计算一个数的倒数。它会检查输入是否为数字以及是否为零,若不符合要求则抛出错误。同时使用

Test::Most

模块对

reciprocal

子例程进行了测试。

28、变量$second、$fourth和$sixth中哪些值为真,已知$second为含单个空格的字符串,$fourth值为‘0.0’,$sixth值为’false’。


`$second`、`$fourth` 和 `$sixth` 的值都为真。因为在 Perl 里,非空字符串(像含单个空格的字符串、`'0.0'`、`'false'`)都会被当作真。

29、创建 @celsius 数组。

一种创建

@celsius

数组的方法如下:


my @fahrenheit = ( 0, 32, 65, 80, 212 );
my @celsius = map { ($_ - 32) * 5/9 } @fahrenheit;

30、代码打印 1 到 10 的数字,若要让这些数字分行打印,该如何修改代码


假设在 Perl 中使用简单的循环打印 1 到 10 的数字,原代码可能类似:

```perl
for (1..10) { print $_; }

若要分行打印,可在 print 语句中添加换行符,修改后的代码如下:


for (1..10) { print $_ . "
"; }

31、给定一个数组,编写代码计算其平均值并打印数组元素和平均值

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


my @numbers = (1, 2, 3, 4, 5); # 示例数组
my $sum = 0;
foreach my $num (@numbers) {
    $sum += $num;
}
my $average = $sum / scalar @numbers;
print "数组元素: ", join(", ", @numbers), "
";
print "平均值: $average
";

这段代码首先定义了一个数组

@numbers

,然后遍历数组计算元素的总和,接着计算平均值,最后打印数组元素和平均值。你可以根据需要修改

@numbers

数组的内容。

32、指出代码中 C – style for 循环的逻辑错误并给出修正方案

在代码示例


my $i = 0;
for (;$i < 10;) {
    $i++;
    print "$i";
}

中,逻辑错误在于打印数字前先执行了

$i++

,导致打印的数字是从

1 到 10

,而不是从

0 到 9

修正方案


$i++

移到

print

语句之后,即:


my $i = 0;
for (;$i < 10;) {
    print "$i";
    $i++;
}

33、在 ‘add your code here’ 处编写代码,为哈希 %stat_for 的每个键赋一个 2 + int(rand(6)) + int(rand(6)) 的随机值


foreach my $stat (keys %stat_for) {
    my $random = 2 + int(rand(6)) + int(rand(6));
    $stat_for{$stat} = $random;
}

34、编写完整代码,为哈希 %stat_for 的 strength、intelligence、dexterity 键赋一个 2 + int(rand(6)) + int(rand(6)) 的随机值,若随机值小于 6 则重新生成,最后打印结果

以下是完整的 Perl 代码:


use strict;
use warnings;

my %stat_for = (
    strength     => undef,
    intelligence => undef,
    dexterity    => undef,
);

foreach my $stat (keys %stat_for) {
    my $random;
    do {
        $random = 2 + int(rand(6)) + int(rand(6));
    } while ($random < 6);
    $stat_for{$stat} = $random;
}

print <<"END_CHARACTER";
Strength: $stat_for{strength}
Intelligence: $stat_for{intelligence}
Dexterity: $stat_for{dexterity}
END_CHARACTER

此代码先定义了哈希

%stat_for

,接着遍历其键,为每个键生成一个

2 + int(rand(6)) + int(rand(6))

的随机值,若值小于 6 就重新生成,最后打印哈希中各键的值。

35、创建一个包含 1 到 5 的数组 @first,通过引用赋值给 @second,打印 @first 和 @second,写出代码


my @first = (1, 2, 3, 4, 5);
my @second = @first;
print "@first
";
print "@second
";

36、给定一个表示销售数据的哈希 $sales,其键为销售人员,值为销售数量,编写代码计算销售总和。

以下是一个示例代码,假设

$sales

是一个哈希引用,其键为销售人员,值为销售数量:


use strict;
use warnings;
use diagnostics;

# 假设的销售数据
my $sales = {
    jim       => 2,
    john      => 7,
    mary      => 1,
    alice     => 4,
    pablo     => 10,
    katherine => 4,
};

my $total_sales = 0;

while ( my ( $salesperson, $num_sales ) = each %$sales ) {
    $total_sales += $num_sales;
}

print "销售总和为: $total_sales
";

这段代码遍历哈希中的每个键值对,将每个销售人员的销售数量累加到

$total_sales

变量中,最后输出销售总和。

37、对于给定的分数哈希表,如何正确获取键为 Jim 和 Mary 对应的分数

可以使用以下两种方式:

使用切片:

perl
my ( $jim, $mary ) = @$score_for{ qw{jim mary} };

单独赋值:

perl
$jim = $score_for->{jim};
$mary = $score_for->{mary};

38、编写一个子程序计算传入数字的平均值,并打印出给定数字列表的平均值

以下是一个用 Perl 编写的示例代码来实现该功能:


sub calculate_average {
    my @numbers = @_;
    my $sum = 0;
    foreach my $num (@numbers) {
        $sum += $num;
    }
    my $average = $sum / scalar @numbers;
    return $average;
}

my @number_list = (1, 2, 3, 4, 5);
my $result = calculate_average(@number_list);
print "给定数字列表的平均值是: $result
";

以上代码定义了一个名为

calculate_average

的子程序,它接收一组数字作为参数,计算这些数字的总和并除以数字的数量得到平均值,最后返回该平均值。在主程序中,我们创建了一个数字列表并调用该子程序,将结果打印输出。

39、请编写代码计算并打印传入数字 1、5、18、3、5 的平均值。

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


numbers = [1, 5, 18, 3, 5]
average = sum(numbers) / len(numbers)
print(average)

若使用 Perl 语言,则代码如下:


#!/usr/bin/perl
use strict;
use warnings;
my @numbers = (1, 5, 18, 3, 5);
my $sum = 0;
foreach my $num (@numbers) {
    $sum += $num;
}
my $average = $sum / scalar @numbers;
print $average, "
";

40、请编写一个名为 average 的子例程,该子例程要能判断传入的参数是否为数字,若传入的参数不是数字则抛出错误,并且计算传入数字参数的平均值。例如,当传入 1 5 18 bob 3 5 时,要能正确处理。

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


use Scalar::Util 'looks_like_number';
use Carp 'croak';

sub average {
    my @numbers = @_;
    my $total = 0;
    foreach my $number (@numbers) {
        if ( not looks_like_number($number) ) {
            croak "$number doesn't look like a number";
        } else {
            $total += $number;
        }
    }
    return $total / @numbers;
}

print average(qw< 1 5 18 bob 3 5>);

41、请编写一个 make_multiplier 子例程,返回一个能将传入参数与指定数字相乘的子例程,并进行测试

以下是实现代码:


sub make_multiplier {
    my $number = shift;
    return sub {
        return shift(@_) * $number;
    };
}
my $times_seven = make_multiplier(7);
my $times_five = make_multiplier(5);
print 21 == $times_seven->(3) ? "yes
" : "no
";
print 20 == $times_five->(4) ? "yes
" : "no
";

上述代码定义了

make_multiplier

子例程,它接受一个数字作为参数,返回一个匿名子例程。匿名子例程接受一个数字并返回该数字与

make_multiplier

传入数字的乘积。之后创建了

$times_seven


$times_five

两个子例程引用,并进行了测试。

42、请编写一个 sum 子例程,使用递归方法计算传入参数的总和,如传入 1, 93, 3, 5

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


sub sum {
    return 0 unless @_;
    my ( $head, @tail ) = @_;
    return $head + sum(@tail);
}

print sum( 1, 93, 3, 5 );

43、编写一个子程序,它接受一个数字作为参数,并返回一个新的子程序,新子程序接受一个参数并将其与传入的数字相乘。

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


sub multiplier_creator {
    my $number = shift;
    return sub {
        my $input = shift;
        return $input * $number;
    };
}

你可以使用以下方式调用这个子程序:


my $multiply_by_five = multiplier_creator(5);
print $multiply_by_five(3); # 输出 15

44、编写一个递归子程序来计算传入参数的总和。

以下是一个在 Perl 中编写的递归子程序,用于计算传入参数的总和:


sub sum_recursive {
    my @numbers = @_;
    if (scalar @numbers == 0) {
        return 0;
    } else {
        my $first = shift @numbers;
        return $first + sum_recursive(@numbers);
    }
}

你可以使用以下方式调用这个子程序:


my $result = sum_recursive(1, 2, 3, 4, 5);
print $result; # 输出 15

45、编写代码,从标准输入读取内容并打印出来。

在Perl中,可以使用以下代码从标准输入读取内容并打印出来:


while (my $line = <STDIN>) {
    print $line;
}

这段代码使用

while

循环不断从标准输入读取每一行内容,将其存储在变量

$line

中,然后使用

print

函数将其打印出来,直到没有更多输入为止。

46、请编写代码实现打印输入内容

在 Perl 中,可使用如下代码实现打印输入内容:


print <STDIN>;

此代码会读取标准输入的内容并将其打印出来。若要逐行读取并打印,可使用循环:


while (my $line = <STDIN>) {
    print $line;
}

47、请编写代码实现跳过空白行和注释行后打印输入内容

以下是使用 Python 实现跳过空白行和注释行后打印输入内容的代码示例:


# 假设输入内容存储在一个列表中,模拟读取文件的行
input_lines = [
    '这是一行正常内容',
    '# 这是注释行',
    ' ',
    '另一行正常内容'
]

# 遍历输入的每一行
for line in input_lines:
    # 去除行首尾的空白字符
    line = line.strip()
    # 检查行是否为空或为注释行(假设注释以 # 开头)
    if line and not line.startswith('#'):
        print(line)

上述代码会遍历输入的每一行,去除行首尾的空白字符,然后检查行是否为空或为注释行,如果不是则打印该行。

如果输入内容来自文件,可以将

input_lines

替换为文件读取的操作,例如:


with open('your_file.txt', 'r', encoding='utf-8') as file:
    for line in file:
        line = line.strip()
        if line and not line.startswith('#'):
            print(line)

这样就可以从文件中读取内容,跳过空白行和注释行后打印。

48、请编写代码将命令行参数中的数字转换为对应的字符并打印出来

以下是使用 Python 实现将命令行参数中的数字转换为对应字符并打印的代码:


import sys
for arg in sys.argv[1:]:
    try:
        num = int(arg)
        char = chr(num)
        print(char)
    except ValueError:
        print(f'{arg} 不是有效的数字。')
    except OverflowError:
        print(f'{arg} 超出字符编码范围。')

在上述代码中,

sys.argv

用于获取命令行参数,

sys.argv[0]

是脚本本身的名称,所以从

sys.argv[1:]

开始遍历参数。对于每个参数,尝试将其转换为整数,再使用

chr()

函数将整数转换为对应的字符并打印。如果参数无法转换为整数,会捕获

ValueError

异常;如果数字超出字符编码范围,会捕获

OverflowError

异常。

49、请编写代码将命令行参数中的每个单词拆分为字符,打印每个字符的 Unicode 码点

以下是一个使用 Perl 编写的示例代码来实现此功能:


#!/usr/bin/perl
use strict;
use warnings;

foreach my $word (@ARGV) {
    my @chars = split //, $word;
    foreach my $char (@chars) {
        my $codepoint = ord($char);
        print "字符: $char, Unicode 码点: $codepoint
";
    }
}

将上述代码保存为一个

.pl

文件,例如

unicode_codepoints.pl

,然后在命令行中运行:


perl unicode_codepoints.pl your words here

它会将每个单词拆分为字符并打印出每个字符的 Unicode 码点。

© 版权声明

相关文章

暂无评论

none
暂无评论...