1

私が達成しようとしている出力は次のようになります。

Name age gpa

各配列のすべての要素に対して、ただし、配列の内容をファイルに書き込むと、どこかに上書きされ、次のように表示されます。

name1 name2 age1

これが私のコードです。すべてのヘルプ(私のコーディングスタイルであっても)は大歓迎です。必要と思われるコードの部分のみを表示しました。さらにヘルプが必要な場合はお知らせください。

sub get_name
{
    printf("Name :");
    push(@get_name, <STDIN>); #taking name from user and putting it in @get_name array
    chomp(@get_name); #erasing newline from input
    #return @get_name;
}       

sub get_age
{
    print "Age :";
    push(@get_age, <STDIN>); #taking name from user and putting it in @get_name array
    chomp(@get_age); #erasing newline from input
    #return @get_age;
}       

sub get_gpa
{
    print "GPA: ";
    push(@get_gpa,<STDIN>);  #taking name from user and putting it in @get_name array
    chomp(@get_gpa); #erasing newline from input
    #return @get_gpa; #returning value may be unnecessary
}       

sub continue
{
    print "Get student data: ";
     $stu_data = <STDIN>;

    if ($stu_data =~ m/[n]/)
    {
            print "\nEnter file name: "; $user_file = <STDIN>;
            open (FILE, ">>$user_file") or die $!;
            printf FILE "%-0.25s %3.3s %4.4s\n", @get_name, @get_age, @get_gpa;           #writing to user given file.
            exit;

    }
    elsif ($stu_data =~ m/[y]/)
    {       #possibly makes do while loop unnecessary
            &get_name;
            &get_age;
            &get_gpa;
            &continue;
    }


}
close FILE;
4

2 に答える 2

1

入力を取得してリストにプッシュする方法によりpush(@get_name, <STDIN>);、ユーザーが Ctrl-D を押すまで、それぞれが行単位で読み込まれます。@get_nameこれは悪いユーザー インターフェースですが、複数の回線をプッシュして友だちにすることもできます。各行は配列の要素です。デモンストレーションに…

$ perl -we '@lines = <STDIN>;  for my $line (@lines) { print "Line: $line"; }'
Foo
Bar
Line: Foo
Line: Bar

これは、ユーザーが入力の終了を期待して Enter キーを 2 回押した場合に発生します。

代わりに欲しいのはmy $name = <STDIN>; push @names, $name;. これは 1 行で読み込まれ$name、自動的に停止します。次に、その単一の名前を にプッシュします@names

次に、出力側に別の問題があります。 printf引数のリストを取ります。Perl は、1 つの要素を持つ 3 つのリストを渡すことと、3 つの要素を持つリストを渡すことの違いを認識していません。それらはすべて 1 つの大きなリストにフラット化されます。したがってprintf FILE "%-0.25s %3.3s %4.4s\n", @get_name, @get_age, @get_gpa、すべての名前、年齢、GPA を 1 つの大きなリストにマッシュアップすると、printf は最初の 3 つだけを出力します。したがって、生徒が 2 人いる場合は、合格ですname1, name2, age1, age2, gpa1, gpa2。ループが必要です。

for my $idx (0..$#get_name) {
    printf FILE "%-0.25s %3.3s %4.4s\n", $get_name[$idx], $get_age[$idx], $get_gpa[$idx];
}

これは、0 から の最大インデックスまでループすることを意味し@get_nameます。これは、1 からループ内の項目数を引いたものです。歴史的な理由から、配列はゼロから数え始めます。次に、各配列の 0 番目、1 番目、2 番目の... エントリを printf にフィードできます。

さらに深く掘り下げると、コードを改善するには 3 つの方法があります。

1 つ目は、サブルーチンの使用を改善することです。グローバル変数を設定するルーチンの代わりに、ルーチンはその結果を返すことができます。

sub get_name
{
    printf("Name :");
    my $name = <STDIN>;
    chomp($name);
    return $name;
}

# @names is a better name because it describes what it contains, not how you got it
push @names, get_name;

残りも同じです。これが完了すると、必要なルーチンが 3 つではなく 1 つだけであることが明らかになります。変更されるのはプロンプトだけです。

sub get_input {
    my($prompt) = @_;  # this pulls in subroutine arguments

    print("$prompt: ");  # no need to use printf here
    my $input = <STDIN>;
    chomp($input);

    return $input;
}

push @names, get_input("Name");
push @ages,  get_input("Age");
push @gpas,  get_input("GPA");

これで、3 つの同じルーチンとバグの 3 つの機会を維持する代わりに、1 つのルーチンができました。

continueこれは Perl のキーワードであるため、サブルーチン名としては適切ではありません。mainこれはプログラムの主要部分であり、典型的な規則であるため、これを呼び出す方がよいでしょう。

それ自体を呼び出すよりもcontinue、再帰と呼ばれるものが非常に便利ですが、ここでは必要ありません。while ループを使用する方が適切です。

sub main {
    while(1) {  # this is how you say "loop forever"
        print "Please enter data about a student.\n";
        push @names, get_input("Name");
        push @ages,  get_input("Age");
        push @gpas,  get_input("GPA");

        print "Would you like to enter more data?\n";
        my $more = <STDIN>;
        last if !$more =~ m/y/;  # last exits the current loop
    }

    print "\nEnter file name to output the data to: ";
    my $data_file = <STDIN>;
    open(my $fh, ">>$data_file") or die $!;

    for my $idx (0..$#names) {
        printf $fh "%-0.25s %3.3s %4.4s\n", $names[$idx], $ages[$idx], $gpas[$idx];
    }
}

あなたprintfには問題があります。.3 の部分には%3.3s、文字列の意味はありません。数値の場合、小数点以下 3 桁まで出力することを意味します。しかし、%sこれは文字列を意味します。私はあなたが何をしているのかわからない。GPA では、2.30 のように印刷することを意図していると仮定します。それは%.2f(プログラマーが「10 進数」と言う紛らわしい方法である「浮動小数点」の f) です。年齢は常に整数になるので、%3d. はい、d for... 整数。

このデータを再度読み込んでフィールドを分離したい場合、これを行う通常の方法は、タブでフィールドを分離することです。次に、読み返すときに各行をタブで分割できます。すべてをまとめると...

    for my $idx (0..$#names) {
        printf $fh "%s\t%3d\t%.2f\n", $names[$idx], $ages[$idx], $gpas[$idx];
    }

調査する次のステップは、データを再構築することです。各生徒に関するデータを複数の配列に分割すると、調整してサブルーチンに渡すのが面倒になります。代わりに、ハッシュを使用します。

my %student;
$student{name} = get_input("Name");
$student{age}  = get_input("Age");
$student{gpa}  = get_input("GPA");
push @students, \%student;

@studentsハッシュ参照のリストが含まれるようになりました。これは少し高度になってきており、少しざっと目を通しているのですが、このように最初の生徒の名前を取得できるようになりました。

my $first_student = $students[0];
my $name = $first_student->{name};

そして、それらをすべて印刷すると、ループは次のようになります。

for my $student (@students) {
    printf $fh "%s\t%3d\t%.2f\n", $student->{name}, $student->{age}, $student->{gpa};
}

全部で、こんな感じです。

use strict;
use warnings;

main();

sub main {
    my @students;

    # Get students
    while(1) {  # this is how you say "loop forever"
        print "Please enter data about a student.\n";
        my %student;
        $student{name} = get_input("Name");
        $student{age}  = get_input("Age");
        $student{gpa}  = get_input("GPA");
        push @students, \%student;

        print "Would you like to enter more data?\n";
        my $more = <STDIN>;
        last unless $more =~ m/y/;  # last exits the current loop
    }

    # Get the file to output to
    print "\nEnter file name to output the data to: ";
    my $data_file = <STDIN>;
    open(my $fh, ">>$data_file") or die $!;

    # Dump the students into the file
    for my $student (@students) {
        printf $fh "%s\t%3d\t%.2f\n", $student->{name}, $student->{age}, $student->{gpa};
    }
}

sub get_input {
    my($prompt) = @_;  # this pulls in subroutine arguments

    print("$prompt: ");  # no need to use printf here
    my $input = <STDIN>;
    chomp($input);

    return $input;
}
于 2013-04-07T19:42:48.847 に答える
0

コードのこの行は、合計で 3 つの要素を出力しています: (1 つの printf と 3 つの%s要素):

printf FILE "%-0.25s %3.3s %4.4s\n", @get_name, @get_age, @get_gpa;

これはあなたがしたいことではありません。基本的にすべての配列を連結し、最初の 3 つの要素を出力します。すべての配列に 2 つの要素がある場合は、 が得られname1 name2 age1ます。すべての配列に 3 つの要素がある場合name1 name2 name3。等。

次のようにしてみてください。

for my $i (0..$#get_name) {
    printf FILE "%-0.25s %3.3s %4.4s\n", $get_name[$i], $get_age[$i], $get_gpa[$i];
}

このコードを perl で記述する方法はいくつかあります。これは c ライクなバージョンです。注意: このループは、3 つの配列すべてが同じ数の要素を持つことを前提としています。

于 2013-04-07T19:03:00.387 に答える