【问题标题】:Perl : Trying to deference an array after sorting itPerl:尝试在排序后取消引用数组
【发布时间】:2015-04-25 06:22:13
【问题描述】:

我目前正在编写一个 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);

【问题讨论】:

  • print Dumper \@sortdown;
  • 什么都不做。我试过了。
  • 我的错误。 print Dumper(\@sortdown); 都一样,我会期待某事,而不是“什么都不做”。
  • “无组织的输出”是什么意思?您是否尝试使用 Dumper 获得整洁的格式化输出?

标签: perl


【解决方案1】:

这里的问题是您需要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) 是明确排序的,因此您将始终以相同的顺序打印学生字段。 (恐怕还没有完全测试你的例子,因为我没有你的源数据,但这种方法适用于样本数据 sn-p)。

如果您还需要打印密钥,那么您可能需要一个 foreach 循环:

foreach my $field ( @field_order ) {
    print "$field => ", $student->{$field},"\n";
}

或者更简洁:

print "$_ => ", $student -> {$_},"\n" for @field_order;

虽然我不确定我是否喜欢这样,但这也许是一个品味问题。

【讨论】:

  • 假设我想用一个字符分隔数组中的每个元素。使用 $, = ',' 是否可以接受
  • 这就是它的用途。不过要稍微谨慎,因为它是全球性的。将其本地化到一个范围是一件好事。但大多数情况下我会说只是使用“加入”来代替
【解决方案2】:

您的错误的本质是假设散列将具有特定的顺序。作为@Sobrique explains,这个假设是错误的。

我假设您正在尝试学习 Perl,因此,一些有关基础知识的指导会很有用:

#!bin/usr/perl

您的 shebang line 错误:在 Windows 上,或者如果您使用 perl script.pl 运行脚本,这无关紧要,但您要确保该行中指定的解释器使用绝对路径。

此外,您可能并不总是希望使用系统附带的 perl 解释器,在这种情况下,#!/usr/bin/env perl 可能对一次性脚本有所帮助。

use strict;
use warnings;
use Data::Dumper;
use 5.010;

我倾向于在 pragmata 之前更喜欢版本约束(utf8 的情况除外)。 Data::Dumper 是一种调试辅助工具,而不是用于人类可读报告的东西。

my $filename = $ARGV[0];

你应该检查你是否确实在命令行上得到了一个参数,如下所示:

@ARGV or die "Need filename\n";
my $filename = $ARGV[0];

open ( INPUT_FILE , '

INPUT_FILE 这样的文件句柄被称为bareword 文件句柄。这些具有包范围。相反,请使用词法文件句柄,您可以将其范围限制为最小的适当块。

没有必要在open 的第三个参数中插入$filename

在因open 中的错误而死时,始终包含文件名和错误消息。用' ' 将文件名括起来有助于您识别任何其他难以检测到的可能导致问题的字符(例如换行符或空格)。

open my $input_fh, '<', $filename
    or die "Could not open '$filename' for reading: $!";
sub readLines{

这是读入您在全局范围内定义的数组。如果您想使用相同的子程序将来自两个不同文件的记录读入两个单独的数组中怎么办? readLines 应该接收一个文件名作为参数,并返回一个 arrayref 作为其输出(见下文)。

   while(my $currentLine = <INPUT_FILE>){
        chomp($currentLine);

在大多数情况下,您希望删除所有尾随空格,而不仅仅是行终止符。

       my @myLine = split(/\s+/,$currentLine);

/\s+/ 上的splitsplit ' ' 不同。在大多数情况下,后者更有用。阅读perldoc -f split 中的差异。

       my %temphash = (
        name => "$myLine[0]",
        age  => "$myLine[1]",
        GPA  => "$myLine[2]",
        MA   => "$myLine[3]"
        );

再次使用无用的插值。无需将这些值插入到新字符串中(除非它们可能是重载字符串化的对象,但在这种情况下,您知道它们只是普通字符串。

       pushToStudents(\%temphash);

在这种情况下,不需要额外的pushToStudents 子例程,除非这是以后能够将数据加载到数据库或其他东西的方法的存根。即使在这种情况下,最好为函数提供回调。

sub pushToStudents{ 我的 $data = 班次; 推送 $students ,$data; }

您正在将数据推送到全局变量。只能有一个学生记录数组的程序是没有用的。

sub printData{
    my $COMMAND = shift;
    if($COMMAND eq "sort 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;
}

【讨论】:

  • 就风格而言,在获取文件参数时,我倾向于不只是从@ARVG 中删除第一个条目——我通常会将其作为foreach 循环来完成。
  • 这是相当不错的,我将尝试将其中一些方法替换到程序中。我对正则表达式不太熟悉。这需要一些时间来阅读!
猜你喜欢
  • 2019-04-10
  • 1970-01-01
  • 1970-01-01
  • 2021-03-23
  • 1970-01-01
  • 2012-06-22
  • 1970-01-01
  • 1970-01-01
  • 2018-02-25
相关资源
最近更新 更多