Подпрограммы против скриптов в Perl - PullRequest
2 голосов
/ 24 августа 2010

Я довольно новичок в Perl, и мне было интересно, каковы лучшие практики в отношении подпрограмм с Perl. Может ли подпрограмма быть слишком большой?

Я сейчас работаю над сценарием, и может потребоваться вызвать другой сценарий. Должен ли я просто интегрировать старый скрипт в новый в виде подпрограммы? Мне нужно передать один аргумент скрипту и мне нужно одно возвращаемое значение.

Полагаю, мне нужно было бы применить какую-то черную магию, чтобы получить результат от оригинального сценария, поэтому подпрограмма имеет смысл, верно?

Ответы [ 4 ]

7 голосов
/ 24 августа 2010

Избегать «черной магии» всегда хорошая идея при написании кода. Вы никогда не захотите перепрыгивать через обручи и придумывать не интуитивный взлом для решения проблемы, особенно если этот код необходимо поддерживать позже. Это случается, правда, и мы все виноваты в этом. Обстоятельства могут сильно повлиять на то, чтобы «просто заставить чертову вещь работать».

Суть в том, что лучшая практика - всегда делать код чистым и понятным. Помните, и это особенно верно с кодом на Perl, по моему опыту, любой код, который вы написали сами более нескольких месяцев назад, также мог быть написан кем-то другим. Поэтому, даже если вы единственный, кому нужно это поддержать, сделайте себе одолжение и сделайте его легким для чтения.

Не цепляйтесь за широкие широкие идеи, такие как «отдавайте предпочтение большему количеству файлов по сравнению с большими файлами» или «отдавайте предпочтение меньшим методам / подпрограммам, чем более крупным» и т. Д. Это хорошие руководящие принципы, чтобы быть уверенными, но применять дух руководящих принципов, а не Письмо об этом. Держите код чистым, понятным и обслуживаемым. Если это означает случайный большой файл или большой метод / подпрограмму, пусть будет так. Пока это имеет смысл.

5 голосов
/ 24 августа 2010

Ключевой целью дизайна является разделение интересов. В идеале каждая подпрограмма выполняет одну четко определенную задачу. В этом свете главный вопрос заключается не в размере подпрограммы, а в ее фокусе . Если вашей программе требуется несколько задач, это подразумевает несколько подпрограмм.

В более сложных сценариях вы можете получить группы подпрограмм, которые логически связаны друг с другом. Они могут быть организованы в библиотеки или, что еще лучше, в модули. Если возможно, вы хотите избежать сценария, в котором у вас есть несколько сценариев, которые должны взаимодействовать друг с другом, потому что обычный механизм возврата данных в другой сценарий одним скриптом утомителен: первый сценарий записывает в стандартный вывод, а второй скрипт должен проанализировать этот вывод.

Несколько лет назад я начал работать на работе, требующей, чтобы я создал большое количество сценариев командной строки (по крайней мере, так оно и было; вначале было неясно, что мы строили). Я был довольно неопытен в то время и не очень хорошо организовал код. Оглядываясь назад, я должен был исходить из того, что я писал модули, а не сценарии. Другими словами, настоящая работа была бы выполнена модулями, а сценарии (код, выполняемый пользователем в командной строке) оставались бы очень маленькими интерфейсами для вызова модулей различными способами. Это способствовало бы повторному использованию кода и тому подобному. Живи и учись, верно?

3 голосов
/ 24 августа 2010

Еще один вариант, который еще не был упомянут для повторного использования кода в ваших сценариях, - это поместить общий код в модуль .Если вы поместите общие подпрограммы в модуль или модули, вы можете сделать ваши скрипты короткими и сфокусированными на том, что они делают, что является особенным, в то же время изолируя общий код в удобной форме для повторного использования.

Например,Вот модуль с несколькими подпрограммами.Поместите это в файл с именем 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 );    

Некоторая связанная документация:

  • perlmod -О модулях Perl в целом
  • perlmodstyle - Руководство по стилю модулей Perl;здесь есть очень полезная информация.
  • perlnewmod - запуск нового модуля
  • Exporter - модуль, используемый для экспорта функций в примере кода
  • use - статья о perlfunc в use.

В некоторых из этих документов предполагается, что вы будете делиться своим кодом на CPAN.Если вы не будете публиковать в CPAN, просто проигнорируйте части о регистрации и загрузке кода.

Даже если вы не пишете для CPAN, для разработки модулей полезно использовать стандартные инструменты и файловую структуру CPAN.Следование стандарту позволяет использовать все инструменты, используемые авторами CPAN, для упрощения процесса разработки, тестирования и установки.

Я знаю, что все это кажется действительно сложным, но стандартные инструменты облегчают каждый шаг.Даже добавление модульных тестов в ваш дистрибутив модуля легко благодаря отличным инструментам.Окупаемость огромна и стоит потраченного времени.

0 голосов
/ 24 августа 2010

Иногда имеет смысл иметь отдельный скрипт, иногда нет.«Черная магия» не так уж и сложна.

#!/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";
...