Как я могу записать Perl, который не похож на C?

Кажется, это известная проблема, см. На трекере проблем JetBrains: https://youtrack.jetbrains.com/issue/IDEA-192373 .

5
задан Ether 6 September 2009 в 20:55
поделиться

15 ответов

Иногда наиболее Perlish, что нужно сделать, это обратиться к CPAN вместо того, чтобы писать какой-либо код вообще.

Вот быстрый и грязный пример использования Net :: CIDR :: Lite и Net :: IP :: Match :: Regexp :

#!/path/to/perl

use strict;
use warnings;

use English;
use IO::File;
use Net::CIDR::Lite;
use Net::IP::Match::Regexp qw(create_iprange_regexp match_ip);


my $cidr = Net::CIDR::Lite->new();

my $ips_fh = IO::File->new();

$ips_fh->open("ips") or die "Can't open 'ips': $OS_ERROR";

while (my $line = <$ips_fh>) {

    chomp $line;

    my ($start, $end) = split /,/, $line;

    my $range = join('-', $start, $end);

    $cidr->add_range($range);

}

$ips_fh->close();

my $regexp = create_iprange_regexp($cidr->list());

foreach my $traffic_fn (@ARGV) {

    my $traffic_fh = IO::File->new();

    $traffic_fh->open($traffic_fn) or die "Can't open '$traffic_fh': $OS_ERROR";

    while (my $ip_address = <$traffic_fh>) {

        chomp $ip_address;

        if (match_ip($ip_address, $regexp)) {
            print $ip_address, "\n";
        }     

    }

    $traffic_fh->close();

}

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я только что ударил что, он прошел минимальное тестирование и не тестировался. Проверки работоспособности, обработка ошибок и комментарии опущены, чтобы уменьшить количество строк. Я не сжимал пустые места, хотя.

Что касается вашего кода: вам не нужно определять свои функции перед их использованием.

15
ответ дан 18 December 2019 в 05:11
поделиться

Это, вероятно, больше похоже на C, но также более простое:

use Socket qw(inet_aton inet_ntoa);

my $ip = ("192.156.255.255");

my $ip_1 = inet_ntoa(pack("N", unpack("N", inet_aton($ip))+1));
print "$ip $ip_1\n";

Обновление: я разместил это перед прочтением всех кода в вопросе. Код здесь просто увеличивает IP-адрес.

0
ответ дан 18 December 2019 в 05:11
поделиться

Я что-то упустил ... будет ли работать любая из вышеперечисленных версий массива? Моды выполняются на переменных, локальных для цикла for. Я думаю, что решение Брэда Гилберта Net :: IP было бы моим выбором. Крис Латс в значительной степени вычистил все остальное так, как я бы сделал.

В качестве отступления - некоторые комментарии о читабельности кажутся мне любопытными. Есть ли меньше [энергичных] жалоб на удобочитаемость синтаксиса Erlang / Lisp, потому что есть ТОЛЬКО ОДИН способ написать в них код?

0
ответ дан 18 December 2019 в 05:11
поделиться

Хотя это будет работать:

use strict;
use warnings;
use 5.010;

use NetAddr::IP;

my %addresses;
# Parse all the ip addresses and record them in a hash.
{
  open( my $ips_file, '<', 'ips') or die;

  local $_; # or my $_ on Perl 5.10 or later
  while( my $line = <$ips_file> ){
    my ($ip, $end_ip) = split ',', $line;
    next unless $ip and $end_ip;

    $ip     = NetAddr::IP->new( $ip, 0 ) or die;
    $end_ip = NetAddr::IP->new( $end_ip ) or die;
    while( $ip <= $end_ip ){
      $addresses{$ip->addr} = 1;
      $ip++;
    }
  }
  close $ips_file
}

# print IP addresses in any of the found ranges
use English;

for my $arg (@ARGV) {
  open(my $traffic, '<',$arg) or die "Can't open $arg $OS_ERROR";
  while( my $ip = <$traffic> ){
    chomp $ip;
    if( $addresses{$ip} ){
      say $ip
    }
  }
  close ($traffic);
}

Я бы по возможности использовал маски масок, потому что это становится еще проще:

use Modern::Perl;
use NetAddr::IP;

my @addresses;
{
  open( my $file, '<', 'ips') or die;

  while( (my $ip = <$file>) =~ s(,.*){} ){
    next unless $ip;
    $ip = NetAddr::IP->new( $ip ) or die;
    push @addresses, $ip
  }

  close $file
}


for my $filename (@ARGV) {
  open( my $traffic, '<', $filename )
    or die "Can't open $filename";

  while( my $ip = <$traffic> ) {
    chomp $ip;
    next unless $ip;

    $ip = NetAddr::IP->new($ip) or next; # skip line on error
    my @match;
    for my $cmp ( @addresses ){
      if( $ip->within($cmp) ){
        push @match, $cmp;
        #last;
      }
    }

    say "$ip => @match" if @match;

    say "# no match for $ip" unless @match;
  }
  close ($traffic);
}

Тестовый файл ips :

192.168.0.1/24
192.168.0.0
0:0:0:0:0:0:C0A8:0/128

Тестовый файл трафика :

192.168.1.0
192.168.0.0
192.168.0.5

Вывод:

# no match for 192.168.1.0/32
192.168.0.0/32 => 192.168.0.1/24 192.168.0.0/32 0:0:0:0:0:0:C0A8:0/128
192.168.0.5/32 => 192.168.0.1/24
2
ответ дан 18 December 2019 в 05:11
поделиться

Вместо этого:


if  ($left_part_1 != $right_part_1 ) { 
    return ($left_part_1 < $right_part_1);
}

вы можете сделать это:


return $left_part_1 < $right_part_1 if($left_part_1 != $right_part_1);

Кроме того, вы можете использовать модуль Fatal , чтобы избежать проверки содержимого на наличие ошибок.

1
ответ дан 18 December 2019 в 05:11
поделиться

Вы можете использовать Acme :: Bleach или Acme :: Morse

2
ответ дан 18 December 2019 в 05:11
поделиться

Единственный критерий «как выглядит мой код» - это насколько легко читать и понимать цель кода (особенно программистами, незнакомыми с Perl), а не то, следует ли он определенному стилю.

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

Ваши коллеги могут подумать, что мой код крайне «нерегулярен», но держу пари, что они точно поняли, что делает код, и могут изменить его, чтобы исправить / расширить его без каких-либо проблем:

моя версия:

#******************************************************************************
# Load the allowable ranges into a hash
#******************************************************************************
my %ipRanges = loadIPAddressFile("../conf/ip.cfg");

#*****************************************************************************
# Get the IP to check on the command line
#*****************************************************************************
my ( $in_ip_address ) = @ARGV;

# Convert it to number for comparison
my $ipToCheckNum = 1 * sprintf("%03d%03d%03d%03d", split(/\./, $in_ip_address));

#*****************************************************************************
# Loop through the ranges and see if the number is in any of them
#*****************************************************************************
my $startIp;
my $endIp;
my $msg = "IP [$in_ip_address] is not in range.\n";

foreach $startIp (keys(%ipRanges))
   {
   $endIp = $ipRanges{$startIp};

   if ( $startIp <= $ipToCheckNum and $endIp >= $ipToCheckNum ) 
      {
      $msg = "IP [$in_ip_address] is in range [$startIp] to [$endIp]\n";
      }
   }

print $msg;

#******************************************************************************
# Function: loadIPAddressFile()
#   Author: Ron Savage
#     Date: 04/10/2009
# 
# Description:
# loads the allowable IP address ranges into a hash from the specified file.
# Hash key is the starting value of the range, value is the end of the range.
#******************************************************************************
sub loadIPAddressFile
   {
   my $ipFileHandle;
   my $startIP;
   my $endIP;
   my $startIPnum;
   my $endIPnum;
   my %rangeList;

   #***************************************************************************
   # Get the arguments sent
   #***************************************************************************
   my ( $ipFile ) = @_;

   if ( open($ipFileHandle, "< $ipFile") )
      {
      while (<$ipFileHandle>)
         {
         ( $startIP, $endIP ) = split(/\,/, $_ );

         # Convert them to numbers for comparison
         $startIPnum = 1 * sprintf("%03d%03d%03d%03d", split(/\./, $startIP));
         $endIPnum   = 1 * sprintf("%03d%03d%03d%03d", split(/\./, $endIP));

         $rangeList{$startIPnum} = $endIPnum;
         }

      close($ipFileHandle);
      }
   else
      {
      print "Couldn't open [$ipFile].\n";
      }

   return(%rangeList);
   }

(Примечание: там есть дополнительные строки "#", чтобы сохранить интервал моего безумия, который всегда ломается при размещении кода здесь)

1
ответ дан 18 December 2019 в 05:11
поделиться

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

У меня есть эта действительно замечательная книга Эффективное программирование на Perl , которую я постоянно перечитываю время от времени , Особенно глава под названием «Идиоматический Perl». Вот несколько вещей, которые я использую для того, чтобы мой Perl выглядел как Perl: операторы списков, такие как map и grep, слайсы и хеш-фрагменты, операторы кавычек.

Еще одна вещь, которая не дает моему Perl выглядеть как FORTRAN / C, это обычные чтение модульных источников, особенно тех из мастеров.

3
ответ дан 18 December 2019 в 05:11
поделиться

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

Серьезно, хотя - лучшее место, чтобы выяснить, как писать на Perl, - это O'Reilly "Perl Best Practices" Дамиана Конвея. Он говорит вам, как он думает, что вы должны что-то делать, и он всегда дает веские основания для своей позиции, а также иногда дает веские основания не соглашаться. Я не согласен с ним по некоторым вопросам, но его аргументация обоснована. Вероятность того, что вы работаете с кем-либо, кто знает Perl лучше, чем мистер Конвей, довольно мала, и наличие печатной книги (или, по крайней мере, подписки на Safari) дает вам более надежную поддержку для ваших аргументов. Возьмите копию Perl Cookbook, пока смотрите ее, так как рассмотрение примеров кода для решения типичных проблем должно помочь вам в правильном направлении. Я ненавижу говорить "купи книгу", но это исключительно хорошие книги, которые должен прочитать любой разработчик Perl.

Что касается вашего конкретного кода, вы используете foreach, $ _ , split без паренов, shift и т. д. Это выглядит довольно перламутрово в моих глазах - которые развивались с Perl уже довольно давно. Одно замечание - я ненавижу английский модуль. Если вы должны его использовать, сделайте это, как , используйте английский qw (-no_match_vars); . Параметр match_vars значительно замедляет синтаксический анализ регулярных выражений, а предоставляемые им переменные $ PREMATCH / $ POSTMATCH обычно бесполезны.

На мой взгляд, это выглядит перламутрово, которые развивались с Perl уже довольно давно. Одно замечание - я ненавижу английский модуль. Если вы должны его использовать, сделайте это, как , используйте английский qw (-no_match_vars); . Параметр match_vars значительно замедляет синтаксический анализ регулярных выражений, а предоставляемые им переменные $ PREMATCH / $ POSTMATCH обычно бесполезны.

На мой взгляд, это выглядит перламутрово, которые развивались с Perl уже довольно давно. Одно замечание - я ненавижу английский модуль. Если вы должны его использовать, сделайте это, как , используйте английский qw (-no_match_vars); . Параметр match_vars значительно замедляет синтаксический анализ регулярных выражений, а предоставляемые им переменные $ PREMATCH / $ POSTMATCH обычно бесполезны.

5
ответ дан 18 December 2019 в 05:11
поделиться

Хотя это, безусловно, является одним из способов сделать это в Perl.

use strict;
use warnings;

my $new_ip;
{
  my @parts = split ('\.', $ip);

  foreach my $part(reverse @parts){
    $part++;

    if( $part > 255 ){
      $part = 0;
      next;
    }else{
      last;
    }
  }
  $new_ip = join '.', reverse @parts;
}

Вот как я бы на самом деле это реализовал.

use NetAddr::IP;

my $new_ip = ''.(NetAddr::IP->new($ip,0) + 1) or die;
13
ответ дан 18 December 2019 в 05:11
поделиться

Большая часть написания кода для Perlish использует преимущества встроенных в Perl функций.

Например, это:

my ($part_1, $part_2, $part_3, $part_4) = split (/\./, $ip);
$part_4++;
if ( $part_4 > 255 ) {
    $part_4 = 0;
    ($part_3++);
    if ( $part_3 > 255 ) {
        $part_3 = 0;
        ($part_2++);
        if ( $part_2 > 255 ) {
            $part_2 = 0;
            ($part_1++);
        }
    }
}   

Я бы переписал что-то вроде:

my @parts = split (/\./, $ip);

foreach my $part(reverse @parts){
  $part++;
  last unless ($part > 255 && !($part = 0));
}

Это делает то, что делает ваш код, опубликованный выше, но немного чище.

Вы уверены, что код делает то, что вы хотите? Просто мне кажется немного странным, что вы переходите к предыдущей «части» IP, только если следующая за ней> 255.

20
ответ дан 18 December 2019 в 05:11
поделиться

Я не могу сказать, что это решение сделает вашу программу более Perl-ish, но это может упростить ваш алгоритм.

Вместо того, чтобы рассматривать IP-адрес как число с точками в квадрате, число-основание 256, которому требуется структура nested-if для реализации функции приращения, рассмотрим IP-адрес как 32-разрядное целое число. Преобразуйте IP-адрес вида abcd в целое число с помощью этого (не проверено):

sub ip2int {
    my $ip = shift;
    if ($ip =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
        return ($1 << 24) + ($2 << 16) + ($3 << 8) + $4;
    } else {
        return undef;
    }
}

Теперь легко определить, находится ли IP-адрес между двумя IP-адресами конечной точки. Просто делайте простую целочисленную арифметику и сравнения.

$begin = "192.168.5.0";
$end = "192.168.10.255";
$target = "192.168.6.2";
if (ip2int($target) >= ip2int($begin) && ip2int($target) <= ip2int($end)) {
    print "$target is between $begin and $end\n";
} else {
    print "$target is not in range\n";
}
6
ответ дан 18 December 2019 в 05:11
поделиться

Другой пример переписать:

sub is_less_than {
    my $left = shift; # I'm sure you just "forgot" to put the my() here...
    my $right = shift;

    my ($left_part_1, $left_part_2, $left_part_3, $left_part_4)     = split (/\./, $left);
    my ($right_part_1, $right_part_2, $right_part_3, $right_part_4) = split (/\./, $right);


    if  ($left_part_1 != $right_part_1 ) { 
        return ($left_part_1 < $right_part_1);
    }   
    if  ($left_part_2 != $right_part_2 ) { 
        return ($left_part_2 < $right_part_2);
    }   
    if  ($left_part_3 != $right_part_3 ) { 
        return ($left_part_3 < $right_part_3);
    }
    if  ($left_part_4 != $right_part_4 ) {
        return ($left_part_4 < $right_part_4);
    }
    return (false);  # They're equal
}

К этому:

sub is_less_than {
    my @left = split(/\./, shift);
    my @right = split(/\./, shift);

    # one way to do it...
    for(0 .. 3) {
        if($left[$_] != $right[$_]) {
            return $left[$_] < $right[$_];
        }
    }

    # another way to do it - let's avoid so much indentation...
    for(0 .. 3) {
        return $left[$_] < $right[$_] if $left[$_] != $right[$_];
    }

    # yet another way to do it - classic Perl unreadable one-liner...
    $left[$_] == $right[$_] or return $left[$_] < $right[$_] for 0 .. 3;

    # just a note - that last one uses the short-circuit logic to condense
    # the if() statement to one line, so the for() can be added on the end.
    # Perl doesn't allow things like do_this() if(cond) for(0 .. 3); You
    # can only postfix one conditional. This is a workaround. Always use
    # 'and' or 'or' in these spots, because they have the lowest precedence.

    return 0 == 1; # false is not a keyword, or a boolean value.
    # though honestly, it wouldn't hurt to just return 0 or "" or undef()
}

Также здесь:

my ($ip, $end_ip, $junk) = split /,/;

$ junk может потребоваться @junk для захвата всех мусора, или , вы, вероятно, можете его отключить - если вы назначите массив неизвестного размера для " массив "из двух элементов, он будет молча отбрасывать все лишние вещи. Итак,

my($ip, $end_ip) = split /,/;

И здесь:

foreach (@ARGV) {
    open(TRAFFIC, $_) or die "Can't open $_ $OS_ERROR";
    while (<TRAFFIC> ) {
        chomp;
        if (defined $addresses{$_}) {
            print "$_\n";
        }
    }
    close (TRAFFIC);
}

Вместо TRAFFIC используйте переменную для хранения дескриптора файла. Кроме того, в общем случае следует использовать exist () , чтобы проверить, существует ли элемент хеша, а не определен () - он может существовать, но быть установлен на undef ] (этого не должно происходить в вашей программе, но это хорошая привычка, когда ваша программа усложняется):

foreach (@ARGV) {
    open(my $traffic, $_) or die "Can't open $_ $OS_ERROR";
    while (<$traffic> ) {
        chomp;
        print "$_\n" if exists $addresses{$_};
    }
    # $traffic goes out of scope, and implicitly closes
}

Конечно, вы также можете использовать замечательный оператор Perl <> , который открывает каждый элемент @ARGV для чтения и действует как файловый дескриптор, который проходит через них:

while(<>) {
    chomp;
    print "$_\n" if exists $addresses{$_};
}

Как было отмечено ранее, старайтесь избегать использования ing английского , если только вы не использовать английский qw (-no_match_vars); , чтобы избежать значительного снижения производительности тех злых match_vars , которые там находятся. И, как еще не было отмечено, но должно быть ...

ВСЕГДА ВСЕГДА ВСЕГДА всегда используют строгие; и используют предупреждения; или Ларри Стена спустится с небес и сломает твой код. Я вижу, у вас есть -w - этого достаточно, потому что даже вне Unix Perl анализирует строку shebang и найдет ваш -w и будет использовать предупреждения; как и должно быть. Тем не менее, вам нужно использовать - использовать строгое; . Это поймает много серьезных ошибок в вашем коде, например, не объявляя переменные с my или используя false в качестве ключевого слова языка.

Заставить ваш код работать в соответствии с строгим , а также предупреждений приведут к НАМНОГО чистому коду, который никогда не сломается по причинам, которые вы не можете понять. Вы потратите часы на отладку отладчика и, возможно, в конечном итоге будете использовать строгие предупреждения и в любом случае, просто чтобы выяснить, что это за ошибки. Только удаляйте их, если (и только если) ваш код завершен и вы освобождаете его, и он никогда не генерирует никаких ошибок.

Работа вашего кода в соответствии с строгими , а также предупреждениями приведет к НАМНОГО более чистому коду, который никогда не сломается по причинам, которые вы не можете понять. Вы потратите часы на отладку отладчика и, возможно, в конечном итоге будете использовать строгие предупреждения и в любом случае, просто чтобы выяснить, что это за ошибки. Только удаляйте их, если (и только если) ваш код завершен и вы освобождаете его, и он никогда не генерирует никаких ошибок.

Работа вашего кода в соответствии с строгими , а также предупреждениями приведет к НАМНОГО более чистому коду, который никогда не сломается по причинам, которые вы не можете понять. Вы потратите часы на отладку отладчика и, возможно, в конечном итоге будете использовать строгие предупреждения и в любом случае, просто чтобы выяснить, что это за ошибки. Только удаляйте их, если (и только если) ваш код завершен и вы освобождаете его, и он никогда не генерирует никаких ошибок.

Вероятно, в конечном итоге я все равно буду использовать строгие предупреждения и , чтобы просто выяснить, в чем заключаются ошибки. Только удаляйте их, если (и только если) ваш код завершен и вы освобождаете его, и он никогда не генерирует никаких ошибок.

Вероятно, в конечном итоге я все равно буду использовать строгие предупреждения и , чтобы просто выяснить, в чем заключаются ошибки. Только удаляйте их, если (и только если) ваш код завершен и вы освобождаете его, и он никогда не генерирует никаких ошибок.

14
ответ дан 18 December 2019 в 05:11
поделиться

Есть только 1 совет: используйте строгий. Остальное вряд ли уместно.

4
ответ дан 18 December 2019 в 05:11
поделиться

Из нескольких лет наблюдения кода Perl, написанного программистами на C, вот несколько общих советов:

Используйте хэши. Используйте списки. ИСПОЛЬЗУЙТЕ HASHES! ИСПОЛЬЗУЙТЕ СПИСКИ! Используйте операции со списками (map, grep, split, join), особенно для небольших циклов. Не используйте модные списочные алгоритмы; pop, splice, push, shift и unshift дешевле. Не используйте деревья; хэши дешевле. Хэши дешевы, делай их, используй их и выбрасывай! Используйте итератор для цикла, а не 3-аргументный. Не называйте вещи $ var1, $ var2, $ var3; используйте вместо этого список. Не называйте вещи $ var_foo, $ var_bar, $ var_baz; используйте вместо этого хэш. Используйте $ foo || = "по умолчанию" . Не используйте $ _ , если вам нужно его напечатать.

Не используйте прототипы, ЭТО ЛОВУШКА !!

Используйте регулярные выражения, не substr () или index () . Люблю регулярные выражения. Используйте модификатор / x , чтобы сделать их читаемыми.

Напишите оператор , если $ foo , когда вы хотите безусловное условие. Почти всегда есть лучший способ написать вложенное условие: попробуйте рекурсию, попробуйте цикл, попробуйте хеш.

Объявляйте переменные, когда они вам нужны, а не в верхней части подпрограммы. использовать строгое. используйте предупреждения и исправьте их все. использовать диагностику. Пишите тесты. Напишите POD.

Используйте CPAN. Используйте CPAN! ИСПОЛЬЗУЙТЕ CPAN! Кто-то, вероятно, уже сделал это, лучше.

Run perlcritic . Запустите его с - брутальным только для ударов. Запустите perltidy . Подумай, зачем ты все делаешь. Измените свой стиль.

Используйте потраченное время на борьбу с языком и отладку выделения памяти, чтобы улучшить свой код.

Задавайте вопросы. Примите стильный комментарий к вашему коду. Перейти на встречу Perl Mongers. Перейти на perlmonks.org. Перейти к YAPC или Perl Workshop. Ваши знания Perl будут стремительно расти.

24
ответ дан 18 December 2019 в 05:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: