私は Perl にかなり慣れていないので、サブルーチンに関するベスト プラクティスが Perl でどのようなものか疑問に思っていました。サブルーチンが大きすぎる可能性はありますか?
私は現在スクリプトに取り組んでおり、別のスクリプトを呼び出す必要があるかもしれません。サブルーチンの形で古いスクリプトを新しいスクリプトに統合するだけですか? スクリプトに 1 つの引数を渡す必要があり、1 つの戻り値が必要です。
元のスクリプトから出力を取得するには、ある種の黒魔術を行う必要があると思います。
私は Perl にかなり慣れていないので、サブルーチンに関するベスト プラクティスが Perl でどのようなものか疑問に思っていました。サブルーチンが大きすぎる可能性はありますか?
私は現在スクリプトに取り組んでおり、別のスクリプトを呼び出す必要があるかもしれません。サブルーチンの形で古いスクリプトを新しいスクリプトに統合するだけですか? スクリプトに 1 つの引数を渡す必要があり、1 つの戻り値が必要です。
元のスクリプトから出力を取得するには、ある種の黒魔術を行う必要があると思います。
コードを書くときは、「黒魔術」を避けるのが常に良い考えです。特にそのコードを後でサポートする必要がある場合は、問題を解決するためにフープを飛び越えて直感的でないハックを考え出すことは決してしたくありません。確かにそれは起こります、そして私たちは皆それに対して有罪です。状況は、「ただのことを機能させるだけ」に重くのしかかる可能性があります。
ポイントは、ベスト プラクティスは常にコードをクリーンで理解しやすいものにすることです。覚えておいてください。私の経験では、これは特にPerl コードに当てはまります。数か月以上前に自分で書いたコードは、他の誰かが書いたものである可能性もあります。したがって、それをサポートする必要があるのがあなただけである場合でも、自分自身に好意を示し、読みやすくしてください。
「大きなファイルよりも多くのファイルを優先する」や「大きなファイルよりも小さなメソッド/サブルーチンを優先する」などの広範な抜本的なアイデアに固執しないでください。これらは確かに良いガイドラインですが、それ。コードをクリーンで理解しやすく、保守しやすいものにします。それが時折の大きなファイルまたは大きなメソッド/サブルーチンを意味する場合は、そうです。それが理にかなっている限り。
主要な設計目標は、関心の分離です。理想的には、各サブルーチンが 1 つの明確に定義されたタスクを実行します。この観点から、主な問題はサブルーチンのサイズではなく、その焦点を中心に展開します。プログラムが複数のタスクを必要とする場合、それは複数のサブルーチンを意味します。
より複雑なシナリオでは、論理的に一緒に属するサブルーチンのグループになる可能性があります。これらは、ライブラリー、またはさらに優れたモジュールに編成できます。あるスクリプトが別のスクリプトにデータを返す通常のメカニズムは面倒なので、可能であれば、複数のスクリプトが相互に通信する必要があるというシナリオは避けたいと考えています。最初のスクリプトは標準出力に書き込み、2 番目のスクリプトは標準出力に書き込みます。スクリプトはその出力を解析する必要があります。
数年前、私は大量のコマンド ライン スクリプトを作成する必要がある仕事に就きました (少なくとも、実際にはそうなりました。最初は、何を作成するのか明確ではありませんでした)。当時、私は経験が浅く、コードをうまく整理できませんでした。後から考えると、スクリプトではなくモジュールを作成しているという前提で作業する必要がありました。つまり、実際の作業はモジュールによって行われ、スクリプト (コマンド ラインでユーザーが実行するコード) は、さまざまな方法でモジュールを呼び出すための非常に小さなフロントエンドのままでした。これにより、コードの再利用とそのすべての優れた機能が促進されます。生きて学びますよね?
スクリプトでコードを再利用するためのまだ言及されていない別のオプションは、共通コードをモジュールに配置することです。共有サブルーチンを 1 つまたは複数のモジュールに配置すると、スクリプトを短くして特別な機能に焦点を当てながら、共通コードを簡単にアクセスして再利用できる形式に分離することができます。
たとえば、いくつかのサブルーチンを含むモジュールがあります。これを次のファイルに入れますMyModule.pm
。
package MyModule;
# Always do this:
use strict;
use warnings;
use IO::Handle; # For OOP filehandle stuff.
use Exporter qw(import); # This lets us export subroutines to other scripts.
# These may be exported.
our @EXPORT_OK = qw( gather_data_from_fh open_data_file );
# Automatically export everything allowed.
# Generally best to leave empty, but in some cases it makes
# sense to export a small number of subroutines automatically.
our @EXPORT = @EXPORT_OK;
# Array of directories to search for files.
our @SEARCH_PATH;
# Parse the contents of a IO::Handle object and return structured data
sub gather_data_from_fh {
my $fh = shift;
my %data;
while( my $line = $fh->readline );
# Parse the line
chomp $line;
my ($key, @values) = split $line;
$data{$key} = \@values;
}
return \%data;
}
# Search a list of directories for a file with a matching name.
# Open it and return a handle if found.
# Die otherwise
sub open_data_file {
my $file_name = shift;
for my $path ( @SEARCH_PATH, '.' ) {
my $file_path = "$path/$file_name";
next unless -e $file_path;
open my $fh, '<', $file_path
or die "Error opening '$file_path' - $!\n"
return $fh;
}
die "No matching file found in path\n";
}
1; # Need to have trailing TRUE value at end of module.
スクリプト A では、ファイル名を検索して処理し、書式設定された出力を出力します。
use strict;
use warnings;
use MyModule;
# Configure which directories to search
@MyModule::SEARCH_PATH = qw( /foo/foo/rah /bar/bar/bar /eeenie/meenie/mynie/moe );
#get file name from args.
my $name = shift;
my $fh = open_data_file($name);
my $data = gather_data_from_fh($fh);
for my $key ( sort keys %$data ) {
print "$key -> ", join ', ', @{$data->{$key}};
print "\n";
}
スクリプト B は、ファイルを検索して解析し、解析したデータ構造を YAML ファイルに書き込みます。
use strict;
use warnings;
use MyModule;
use YAML qw( DumpFile );
# Configure which directories to search
@MyModule::SEARCH_PATH = qw( /da/da/da/dum /tutti/frutti/unruly /cheese/burger );
#get file names from args.
my $infile = shift;
my $outfile = shift;
my $fh = open_data_file($infile);
my $data = gather_data_from_fh($fh);
DumpFile( $outfile, $data );
関連ドキュメント:
use
。これらのドキュメントの一部は、CPAN でコードを共有することを前提としています。CPAN に公開しない場合は、サインアップとコードのアップロードに関する部分は無視してください。
CPAN 用に書いていない場合でも、モジュール開発に標準ツールと CPAN ファイル構造を使用することは有益です。標準に従うことで、CPAN の作成者が開発、テスト、およびインストールのプロセスを簡素化するために使用するすべてのツールを使用できます。
これらすべてが非常に複雑に思えることはわかっていますが、標準ツールを使用すると、各ステップが簡単になります。利用可能な優れたツールのおかげで、ユニット テストをモジュール ディストリビューションに追加することさえ簡単です。見返りは非常に大きく、投資する時間に見合うだけの価値があります。
別のスクリプトを用意することが理にかなっている場合もあれば、そうでない場合もあります。「黒魔術」はそれほど複雑ではありません。
#!/usr/bin/perl
# square.pl
use strict;
use warnings;
my $input = shift;
print $input ** 2;
#!/usr/bin/perl
# sum_of_squares.pl
use strict;
use warnings;
my ($from, $to) = @ARGV;
my $sum;
for my $num ( $from .. $to ) {
$sum += `square.pl $num` // die "square.pl failed: $? $!";
}
print $sum, "\n";
IPC::System::Simple を使用すると、失敗に関するより簡単で優れたエラー報告が自動的に行われます。
#!/usr/bin/perl
# sum_of_squares.pl
use strict;
use warnings;
use IPC::System::Simple 'capture';
my ($from, $to) = @ARGV;
my $sum;
for my $num ( $from .. $to ) {
$sum += capture( "square.pl $num" );
}
print $sum, "\n";