我目前正在编写一个perl脚本,其中我引用了一个引用数组(学生)。将散列引用添加到数组之后。现在,我将引用添加到学生数组中,然后询问用户如何对它们进行排序。这里会让人感到困惑。我不知道如何尊重排序的数组。使用dumper,我可以得到排序数组,但在一个无组织的输出中。如何在排序后对哈希引用数组进行排序?
#!bin/usr/perl
use strict;
use warnings;
use Data::Dumper;
use 5.010;
#reference to a var $r = \$var; Deferencing $$r
#reference to an array $r = \@var ; Deferencing @$r
#referenc to a hash $r = \%var ; deferencing %$r
my $filename = $ARGV[0];
my $students = [];
open ( INPUT_FILE , '<', "$filename" ) or die "Could not open to read \n ";
sub readLines{
while(my $currentLine = <INPUT_FILE>){
chomp($currentLine);
my @myLine = split(/\s+/,$currentLine);
my %temphash = (
name => "$myLine[0]",
age => "$myLine[1]",
GPA => "$myLine[2]",
MA => "$myLine[3]"
);
pushToStudents(\%temphash);
}
}
sub pushToStudents{
my $data = shift;
push $students ,$data;
}
sub printData{
my $COMMAND = shift;
if($COMMAND eq "sort up"){
my @sortup = sort{ $a->{name} cmp $b->{name} } @$students;
print Dumper @sortup;
}elsif($COMMAND eq "sort down"){
my @sortdown = sort{ $b->{name} cmp $a->{name} } @$students;
print Dumper @sortdown;
//find a way to deference so to make a more organize user friendly read.
}else{
print "\n quit";
}
}
readLines();
#Output in random, the ordering of each users data is random
printf"please choose display order : ";
my $response = <STDIN>;
chomp $response;
printData($response);发布于 2015-04-25 10:27:27
这里的问题是,您需要Dumper提供一个有组织的输出。它没有。它转储数据结构以使调试更容易。关键问题是散列是显式无序的数据结构--它们是键值映射,不会产生任何输出顺序。
参考perldata
请注意,仅仅因为哈希是按该顺序初始化的,并不意味着它是按该顺序出现的。
特别是keys函数:
散列项以明显的随机顺序返回。实际的随机顺序特定于给定的哈希;对两个散列的相同操作可能导致每个散列的顺序不同。
perlsec中有一个完整的部分详细地解释了这一点,但只需说明-散列是随机顺序,这意味着当您按名称对学生排序时,没有对每个学生的键值对进行排序。
我建议不要:
my @sortdown = sort{ $b->{name} cmp $a->{name} } @$students;
print Dumper @sortdown;你最好用一个slice
my @field_order = qw ( name age GPA MA );
foreach my $student ( sort { $b -> {name} cmp $a -> {name} } @$students ) {
print @{$student}{@field_order}, "\n";
}数组(@field_order)是显式排序的,因此您将始终以相同的顺序打印您的学生字段。(恐怕您的示例还没有完全测试,因为我没有您的源数据,但是这种方法适用于示例数据片段)。
如果您也需要打印这些键,那么您可能需要一个foreach循环:
foreach my $field ( @field_order ) {
print "$field => ", $student->{$field},"\n";
}或者更简洁:
print "$_ => ", $student -> {$_},"\n" for @field_order;虽然我不太喜欢,但这可能是口味的问题。
发布于 2015-04-25 11:19:06
错误的本质是假设散列会有一个特定的顺序。作为@Sobrique explains,这种假设是错误的。
我假设您正在尝试学习Perl,因此,对基本知识的一些指导将是有用的:
#!bin/usr/perl
您的shebang line是错误的:在perl script.pl上运行脚本并不重要,但是要确保该行中指定的解释器使用绝对路径。
此外,您可能并不总是希望使用系统附带的perl解释器,在这种情况下,#!/usr/bin/env perl可能有助于一次性脚本。
使用严格;使用警告;使用数据::翻车;使用5.010;
在执行之前,我倾向于使用版本约束(utf8除外)。Data::Dumper是一种调试辅助工具,而不是用于人类可读的报表。
我的$filename = $ARGV;
您应该检查是否确实在命令行上获得了一个参数,如下所示:
@ARGV or die "Need filename\n";
my $filename = $ARGV[0];打开( INPUT_FILE,'<',"$filename“)或死”无法打开读\n ";
文件句柄(如INPUT_FILE )称为裸字文件句柄。他们有包范围。相反,使用词法文件句柄,其作用域可以限制为最小的适当块。
不需要在$filename的第三个参数中插入open。
在open中因错误而死亡时,始终包括文件名和错误消息。用' '包围文件名可以帮助您识别可能导致问题的任何其他难以检测的字符(例如换行符或空格)。
open my $input_fh, '<', $filename
or die "Could not open '$filename' for reading: $!";子readLines{
这是读入您在全局范围中定义的数组。如果您想使用相同的子例程将两个不同文件中的记录读取到两个单独的数组中,那么readLines应该接收一个文件名作为参数,并返回一个arrayref作为它的输出(参见下面)。
while(my $currentLine = <INPUT\_FILE>){ chomp($currentLine);
在大多数情况下,您希望删除所有尾随空格,而不仅仅是行终止符。
my @myLine = split(/\s+/,$currentLine);
split on /\s+/不同于split ' '。在大多数情况下,后者更有用。阅读perldoc -f split的不同之处。
my %temphash = ( name => "$myLine[0]", age => "$myLine[1]", GPA => "$myLine[2]", MA => "$myLine[3]" );
再来一次没用的插值。不需要将这些值插入到新的字符串中(除非它们可能是重载字符串的对象,但在本例中,您知道它们只是普通字符串。
pushToStudents(\%temphash);
在这种情况下,不需要额外的pushToStudents子例程,除非这是一个稍后能够将数据加载到数据库或其他地方的方法的存根。即使在这种情况下,对函数提供回调也更好。
子pushToStudents{ my $data = shift;push $students,$data;}
您正在将数据推送到全局变量。一个只有一个学生记录数组的程序是没有用的。
{ my $COMMAND = shift;if($COMMAND eq“$COMMAND up"){
不要这样做。每个子例程都应该有一个明确的目的。
这是您程序的修订版。
#!/usr/bin/env perl
use 5.010;
use strict;
use warnings;
use Carp qw( croak );
run(\@ARGV);
sub run {
my $argv = $_[0];
@$argv
or die "Need name of student records file\n";
open my $input_fh, '<', $argv->[0]
or croak "Cannot open '$argv->[0]' for reading: $!";
print_records(
read_student_records($input_fh),
prompt_sort_order(),
);
return;
}
sub read_student_records {
my $fh = shift;
my @records;
while (my $line = <$fh>) {
last unless $line =~ /\S/;
my @fields = split ' ', $line;
push @records, {
name => $fields[0],
age => $fields[1],
gpa => $fields[2],
ma => $fields[3],
};
}
return \@records;
}
sub print_records {
my $records = shift;
my $sorter = shift;
if ($sorter) {
$records = [ sort $sorter @$records ];
}
say "@{ $_ }{ qw( age name gpa ma )}" for @$records;
return;
}
sub prompt_sort_order {
my @sorters = (
[ "Input order", undef ],
[ "by name in ascending order", sub { $a->{name} cmp $b->{name} } ],
[ "by name in descending order", sub { $b->{name} cmp $a->{name} } ],
[ "by GPA in ascending order", sub { $a->{gpa} <=> $b->{gpa} } ],
[ "by GPA in descending order", sub { $b->{gpa} <=> $a->{gpa} } ],
);
while (1) {
print "Please choose the order in which you want to print the records\n";
print "[ $_ ] $sorters[$_ - 1][0]\n" for 1 .. @sorters;
printf "\n\t(%s)\n", join('/', 1 .. @sorters);
my ($response) = (<STDIN> =~ /\A \s*? ([1-9][0-9]*?) \s+ \z/x);
if (
$response and
($response >= 1) and
($response <= @sorters)
) {
return $sorters[ $response - 1][1];
}
}
# should not be reached
return;
}https://stackoverflow.com/questions/29861771
复制相似问题