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

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

上述代码中,

unique

子例程通过遍历数组,使用哈希表

%seen

来记录已经出现过的元素,将未出现过的元素添加到

@unique

数组中,从而保证返回的元素是唯一且按原数组顺序排列的。之后使用

Test::Most

模块进行测试,确保

unique

函数的输出符合预期。

29、修改 unique 子程序的测试,通过对实际数组和预期数组进行排序来确保元素按顺序排列。

可以使用如下代码修改测试:


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

这样就对

@have


@want

数组都进行了排序,保证了元素按顺序排列。

30、编写一个名为 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

模块,使用

throws_ok

函数测试传入非数字参数时是否抛出预期的错误,同时添加了

is

函数测试正常数字输入的情况。

31、strict和warnings之间的主要区别是什么?

strict

会在程序遇到不安全代码(如使用未声明的变量)时阻止其运行;

warnings

不会阻止程序运行,而是在代码出现可能有问题的行为(如尝试使用未初始化的变量或给字符串加数字)时发出“警告”。

32、已知华氏温度数组 @fahrenheit 的值为 ( 0, 32, 65, 80, 212 ),创建对应的摄氏温度数组 @celsius。

要创建

@celsius

数组,需要将

@fahrenheit

数组中的每个华氏温度值转换为摄氏温度。转换公式是先减去 32,然后乘以 5/9。以下是实现代码:


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

33、给定包含值 (AAA bbb Ccc ddD EEE) 的 @ids 数组,创建 @upper 数组,该数组中的元素为 @ids 数组元素对应的大写形式。

可以使用循环遍历

@ids

数组,将每个元素转换为大写后添加到

@upper

数组中,示例代码如下:


my @ids = qw(AAA bbb Ccc ddD EEE);
my @upper;
foreach my $id (@ids) {
    push @upper, uc($id);
}

34、如何创建一个哈希,键为水果名称,值为它们的正常颜色,并将每个键/值对作为单独的一行打印出来,格式类似于“bananas are yellow”?

可以使用以下代码实现:


use strict;
use warnings;
use diagnostics;
my %color_for = ( 
    bananas => 'yellow', 
    apples => 'red', 
    oranges => 'orange',
);
for my $fruit (keys %color_for) {
    my $color = $color_for{$fruit};
    print "$fruit are $color
";
}

35、如何根据给定的@fahrenheit数组创建@celsius数组?

要根据给定的

@fahrenheit

数组创建

@celsius

数组,需将

@fahrenheit

中的华氏温度转换为摄氏温度。转换公式是先减去 32,再乘以 5/9。示例代码如下:


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

36、分析$answer1、$answer2、$answer3的值,并解释原因,若对自增运算符有困惑,如何重写代码使其更易理解?

$answer1

的值为 28,因为乘法运算符优先级高于加法。

$answer2

初始值为 6,因为减法是左结合的,第三行自增后值为 7。

$answer3

的值为 4,因为

$answer2

在自增前会先从 10 中减去;若自增运算符在

$answer2

前(

10 - ++$answer2

),

$answer3

的值为 3。

若对自增运算符有困惑,可将代码重写为:


my $answer2 = 9 - 2 - 1;
my $answer3 = 10 - $answer2;
$answer2++;

让自增和自减运算符单独成句,代码更易理解。

37、分析代码中变量 $answer1、$answer2 和 $answer3 的值及原因,代码如下:(假设代码中包含乘法、加法、减法和自增运算符,此处虽未给出代码,但不影响对变量值及原因的分析逻辑)


$answer1 的值为 28,因为乘法运算符优先级高于加法。  
$answer2 初始值为 6,因为减法是左结合的,第三行自增操作后其值变为 7。  
$answer3 的值为 4,因为在 $answer2 自增前,它会先被从 10 中减去;若自增运算符在 $answer2 前(即 `10 - ++$answer2`),$answer3 的值将为 3。

38、代码打印结果为 12345678910,若要让这些数字分行打印,该如何修改代码

在不同编程语言中实现方式不同。以 Python 为例,可将数字存储在列表里,然后用循环逐个打印并换行,代码如下:


nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for num in nums:
    print(num)

若是 Java,可使用

System.out.println

语句,代码如下:


for (int i = 1; i <= 10; i++) {
    System.out.println(i);
}

39、给出计算数组 @numbers(值为 3 9 0 7 8)平均值的代码

以下是计算数组

@numbers

平均值的 Perl 代码:


my @numbers = (3, 9, 0, 7, 8);
my $sum = 0;
foreach my $num (@numbers) {
    $sum += $num;
}
my $average = $sum / scalar @numbers;
print "数组的平均值是: $average
";

这段代码首先定义了数组

@numbers

,然后通过循环累加数组元素的值得到总和,最后用总和除以数组元素的数量得到平均值并输出。

40、在给定代码中“add your code here”处添加代码,实现对哈希 %stat_for 中每个键对应的值进行随机赋值(范围为 2 加上两个 1 – 6 的随机整数之和)

可在“add your code here”处添加如下代码:


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

这段代码遍历哈希

%stat_for

的每个键,为每个键生成一个 2 加上两个 1 – 6 随机整数的和作为值,并将其赋给对应的键。

41、写出完整代码,实现对哈希 %stat_for(键为 strength、intelligence、dexterity)中每个键对应的值进行随机赋值(范围 2 + 两个 1 – 6 的随机整数,值小于 6 时重新赋值),并打印结果


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

42、在代码中,使用合适的方式创建并赋值一个包含 1 到 5 的数组 @first ,并创建 @second 数组,使其内容与 @first 相同,该如何实现?

在 Perl 中可以这样实现:


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

43、给定一个表示销售数据的哈希引用 $sales ,计算周五 Jim 的销售数量和销售总额并输出,该如何编写代码?

可以使用以下代码实现:


my %sales = (
    friday => {
        jim => [ 1, 1, 5 ],
        mary => [ 2 ]
    },
);
my $friday = $sales{friday}{jim};
my $num_sales = @$friday;
my $total = 0;
$total += $_ foreach @$friday;
print "Jim had $num_sales sales, for a total of $total dollars
";

44、给定一个表示分数的哈希引用 $score_for ,获取 Jim 和 Mary 的分数并输出,有哪些实现方式以及可能遇到的问题如何解决?

实现方式有两种:

第一种是使用切片,代码为

perl
my ( $jim, $mary ) = @$score_for{ qw{Jim Mary} }; print "$jim $mary";

第二种是单独赋值,代码为

perl
$jim = $score_for->{Jim}; $mary = $score_for->{Mary}; print "$jim $mary";

可能遇到的问题是写

%$score_for{ ... }

,解决办法有两种:

一是使用

@

符号表示获取变量列表

二是不使用切片,单独为变量赋值

45、编写一个子例程 average 来计算传入数字的平均值,并对给定的数字列表计算平均值并输出,该如何实现?

实现代码如下:


sub average {
    my @numbers = @_;
    my $total = 0;
    $total += $_ foreach @numbers;
    return $total / @numbers;
}
print average(qw< 1 5 18 3 5>);

此代码定义了

average

子例程,它接收一组数字,计算这些数字的总和,然后除以数字的数量得到平均值。最后,调用该子例程并传入

1 5 18 3 5

这组数字,将计算结果输出。

46、编写代码计算并输出传入数字的平均值,如传入 1 5 18 3 5 ,输出其平均值

以下是使用 Python 语言实现该功能的代码:


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

如果输入的数字是通过命令行参数传入的,可以使用以下代码:


import sys

numbers = [int(i) for i in sys.argv[1:]]
print(sum(numbers) / len(numbers))

将上述代码保存为一个 Python 文件,例如

average.py

,然后在命令行中运行

python average.py 1 5 18 3 5

即可计算并输出这些数字的平均值。

47、编写一个平均函数,当传入的参数不是数字时,抛出错误,如传入 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>);

上述代码中,

average

函数会遍历传入的参数,使用

looks_like_number

函数检查每个参数是否为数字,如果不是则使用

croak

函数抛出错误,若是数字则累加到

$total

中,最后计算并返回平均值。

48、编写一个递归函数 sum 来计算传入参数的总和,如传入 1, 93, 3, 5 计算其总和

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


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

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

在上述代码中,定义了一个名为

sum

的递归函数。当传入的参数为空时,函数返回 0;否则,取出第一个参数

$head$,并递归调用

sum

函数计算剩余参数的总和,最后将

$head` 与剩余参数的总和相加并返回。

49、编写一个程序,将文本中的日期格式从 YYYY-MM-DD 转换为 Month Day, Year 的格式。

以下是一个使用 Python 实现将日期格式从

YYYY-MM-DD

转换为

Month Day, Year

格式的示例代码:


import datetime

def convert_date(date_str):
    try:
        date_obj = datetime.datetime.strptime(date_str, '%Y-%m-%d')
        new_date_str = date_obj.strftime('%B %d, %Y')
        return new_date_str
    except ValueError:
        return '输入的日期格式不正确,请使用 YYYY-MM-DD 格式。'

# 测试示例
date_str = '2024-05-12'
print(convert_date(date_str))

上述代码定义了一个

convert_date

函数,它接受一个字符串形式的日期,使用

datetime

模块将其转换为

datetime

对象,然后使用

strftime

方法将日期格式化为

'Month Day, Year'

的形式。如果输入的日期格式不符合

'YYYY-MM-DD'

,则会捕获

ValueError

并返回错误信息。

50、编写一个程序,从标准输入读取内容并打印出来。

以下是一个使用 Perl 语言实现从标准输入读取内容并打印出来的示例程序:


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

将上述代码保存为一个

.pl

文件,例如

read_stdin.pl

,然后在命令行中运行

perl read_stdin.pl

,之后在终端输入内容,按回车键即可看到输入的内容被打印出来。输入完成后,可通过按

Ctrl + D

(在 Windows 上按

Ctrl + Z

再按回车键)结束输入。

51、编写一个子程序,返回一个新的子程序,该新子程序用于将输入的数字乘以一个固定的数字。

以下是一个用 Perl 语言实现的示例代码:


sub multiplier {
    my $fixed_num = shift;
    return sub {
        my $input_num = shift;
        return $input_num * $fixed_num;
    };
}

# 使用示例
my $double = multiplier(2);
print $double->(5); # 输出 10

在上述代码中,

multiplier

子程序接收一个固定的数字作为参数,然后返回一个新的匿名子程序。这个新的子程序接收一个输入数字,并将其乘以之前传入的固定数字。

52、编写一个递归子程序来计算一组数字的总和。

以下是一个使用 Perl 编写的递归子程序来计算一组数字的总和的示例代码:


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

# 测试代码
my @test_numbers = (1, 2, 3, 4, 5);
my $result = sum_recursive(@test_numbers);
print $result; # 输出 15

在上述代码中,

sum_recursive

子程序接收一组数字作为参数。如果传入的数组为空,则返回 0;否则,取出数组的第一个元素,将其与剩余元素组成的数组递归调用

sum_recursive

子程序的结果相加。

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

以下是使用 Python 语言实现打印输入内容的代码示例:


input_content = input("请输入内容:")
print(input_content)

上述代码中,

input()

函数用于获取用户输入的内容,然后使用

print()

函数将输入的内容打印出来。

54、编写代码跳过只包含空白字符的行和以#开头的注释行,并打印其他行

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


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)

请将

your_file.txt

替换为你实际要处理的文件路径。此代码会打开文件,逐行读取内容,去除每行首尾的空白字符,若该行不为空且不以

#

开头,则打印该行。

© 版权声明

相关文章

暂无评论

none
暂无评论...