LINUX.ORG.RU

Аргументы функции в perl

 


0

1

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

sub func {
   my ($param1,$param2) = @_;

После этого или до этого, мне ещё надо проверить на too many/few parameters. В других языках эта обязанность возложена на среду, т.е. python код

def func(param1,param2):
   print param1,param2

func(1)

Вызовет:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes exactly 2 arguments (1 given)

Дальше ещё начинают фишки с дефолтным значением и необязательными значениями. В perl это все обязательно делать руками каждый раз?

★★★★

Чтоб получить читабельный код я практически в каждой функции делаю что-то на подобии

Когда я только начинал знакомиться с Перлом, меня тоже пугали переменные по умолчанию. Но потом привык, да так, что уже лет 5 на Перле не кодил, а всё равно по ним скучаю.

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

kranky ★★★★★ ()
Последнее исправление: kranky (всего исправлений: 1)

Частично проблема решается прототипами функций: http://perldoc.perl.org/perlsub.html#Prototypes

Например такой скрипт вызовет ошибку на этапе компиляции

sub t ($$) {
	my ($a, $b) = @_;
	print "Test\n";
}

t(1);
Not enough arguments for main::t at /tmp/x.pl line 6, near "1)"
Execution of /tmp/x.pl aborted due to compilation errors.

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

Olegymous ★★ ()

В других языках эта обязанность возложена на среду

Сомнительное преимущество, но вы можете использовать Sub::Signatures, если так уж приспичило.

no-such-file ★★★★★ ()
Ответ на: комментарий от kranky

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

Это если аргументы одной природы, а если разной?

zloelamo ★★★★ ()
Ответ на: комментарий от no-such-file

И вообще, поиск в CPAN по слову signatures выдаст вам тонну подобных модулей на все случаи жизни.

no-such-file ★★★★★ ()
Ответ на: комментарий от no-such-file

Сомнительное преимущество, но вы можете использовать Sub::Signatures, если так уж приспичило.

Уже лучше, правда cpan использовать не могу (скрипт должен быть автономным и переносимым) - придется так тащить за собой.

zloelamo ★★★★ ()

Perl довольно либерален в передаче аргументов - это круто, но неудобно

Зато есть выбор. Есть встроенные прототипы (выше пример), есть модули меняющие семантику на уровне интерпретатора (прагмы) и на уровне пакета:

http://search.cpan.org/~brentdax/Perl6-Parameters-0.03/Parameters.pm

http://search.cpan.org/~mauke/Function-Parameters-1.0101/lib/Function/Paramet...

Есть гуарды, валидирующие массив аргументов функции. На Params::Validate я забил, и использую свой велосипед. Помогает как в суровом легаси, так и при быстром прототипировании.

outtaspace ★★★ ()
Ответ на: комментарий от zloelamo

Это если аргументы одной природы, а если разной?

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

kranky ★★★★★ ()
Ответ на: комментарий от kranky

Разной - это например массивы и хэши?

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

Вобщем способов не мало, всё зависит от конкретного случая.

Меня интересует как раз общий случай, который я описал в начале. Писать в каждой функции if (length(@_)<2) {rise_fatal_error;};my ($param1,$param2) = @_; чистая мастурбация - этот код не несет никакого практического смысла.

zloelamo ★★★★ ()
Ответ на: комментарий от Reset

Да, видел, но ООП не поддерживает, а мне бы хотелось.

zloelamo ★★★★ ()
Ответ на: комментарий от Olegymous

Частично проблема решается прототипами функций.

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

zloelamo ★★★★ ()
Ответ на: комментарий от Olegymous

А также то, что прототипы не работают в ООП методах.

И наверное в рантайм подключаемых модулях тоже не взлетит? А мне без этого нельзя - я на старте не знаю с какими компонентами буду работать.

zloelamo ★★★★ ()

Проникнись духом перла и тебе все остальное покажется унылым говном:

sub f() {
  my $p1 = shift or die "Xaxaxaxaxaxaxa ".$!;
  my $p2 = shift or die "Muxoxoxoxoxoxo ".$!;
  return $p1.$p2;
}

Jetty ★★★★★ ()
Последнее исправление: Jetty (всего исправлений: 2)
Ответ на: комментарий от zloelamo

Оба могут быть скаляром, но различаются логически.

Интерпретатор про эти «логические» различия ничего не знает, и знать не должен, они существуют только в голове программиста.

length(@_)

$#_

этот код не несет никакого практического смысла

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

 
sub foo {
    my %args = @_;
    my $color = $args{color};
    my $number = $args{number};
  }
  
foo(color=>'red', number=>13);
kranky ★★★★★ ()
Ответ на: комментарий от kranky

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

Я похоже к этому и иду.

zloelamo ★★★★ ()
Ответ на: комментарий от kranky

Интерпретатор про эти «логические» различия ничего не знает, и знать не должен, они существуют только в голове программиста.

Они существую в коде. Это я твоему пассажу о любом числе параметров.

zloelamo ★★★★ ()
#!/usr/bin/perl

use strict;
use warnings;

use JSON::XS;
use Scalar::Util qw/blessed/;

sub f {
  my $un = 0; # unblessed
  my $bl = 0; # blessed

  eval { (blessed $_ ? $bl++ : $un++) for (@_); $bl }
  and $bl == 2 and $un == 1
  and @_ == 3
    or die "f: expect 2 blessed objects and 1 unblessed,\n\t"
          ."but got blessed $bl and unblessed $un objects";

  print shift->pretty->utf8->encode({hello => "lor"});
  $\ = "\n";
  print shift->pretty->utf8->encode({i => "love $_[-1]"});
}

eval {
  f(JSON::XS->new, JSON::XS->new);
};

print $@ if ($@);

eval {
  f(JSON::XS->new, JSON::XS->new, (bless [], "smth"), 1, 2, 3);
};

print $@ if ($@);


f(JSON::XS->new, JSON::XS->new, "perl");

Делай все что хочешь, лучше не придумаешь языка :)

gh0stwizard ★★★★★ ()
Последнее исправление: gh0stwizard (всего исправлений: 1)

Кстати, обсуждаемые здесь subroutine signatures, возможно, войдут в одну из ближайших версий перла и будут доступны из коробки. Последнее время вопрос часто поднимался в списке рассылки perl5-porters. Вот один из последних топиков: http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2013-02/msg00418.html

Olegymous ★★ ()
Ответ на: комментарий от zloelamo

Тоже можно. Будешь ловить ошибки в рантайме. Вообще рекомендую забыть про проверки кода на этапе компиляции. Это тебе не питон и не ява. И именно засчет таких упрощений перл быстро запускает код.

И золотое правило: мусор на входе - мусор на выходе. Тебе так или иначе придется проверять входные данные.

Хотя если это скриптик на 100 строчек для себя - не замарачивайся и пиши на шелле, питоне или еще чем-то, где компиляция якобы делает за тебя всю грязную работу. Перл просто не для тебя :-)

gh0stwizard ★★★★★ ()
Ответ на: комментарий от gh0stwizard

Я бы не назвал это «скриптиком», хотя изначально он именно так и задумывался. Просто есть адовая штука написанная на чистом sh. Размер около 2000 строк. Её надо переписать при любых раскладах, ибо замусорилась, а врисовать туда новый функционал потянет ещё на 2000 строк.

Поэтому я решил переписать на более приличном языке. Выбора у меня особого нет: только perl работает везде (python нет на aix).

При таком объеме мне надо будет разбить проект на модули (там это очень красиво и логично собирается потом), которые потенциально потом будут дописывать другие. И чтобы избежать боли и криков базовый код хочу написать максимально аккуратно, со всеми проверками. И перл, меня несколько шокировал в этом плане.

zloelamo ★★★★ ()
Ответ на: комментарий от zloelamo

Это не мастурбация. Глянь на код известных модулей на cpan. За передачу всякого шлака словишь croak или die. В остальных случаях спецификация интерфейса тебе все объяснит: какие аргументы в какие функции можно и не можно передавать. Под спецификацией интерфейса понимается readme, а чаще pod текст в самом модуле. Добро пожаловать в мир перл.

Нет, можно идти методом тыка и ловить ошибки, а чаще баги в плохом коде, где нет проверок на входные данные.

То что в перле это делается не так как в других языках это особенность языка, а не его изъян.

gh0stwizard ★★★★★ ()
Ответ на: комментарий от gh0stwizard

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

zloelamo ★★★★ ()
Ответ на: комментарий от zloelamo

Рассуждения идеалиста. Хороший код на перле, как и везде это: документация и проверка входных данных. Красивый код в перле это когда ты пишешь примерно в таком стиле как указано выше. Используемые функции над аргументами: shift, pop и splice. Плюс понимание динамической типизации: @_ == 1, один из примеров. В итоге, через какое-то время научишься писать в одной-двух строках код, который будет проверять количество аргументов и динамически их распределять (если потребуется).

Можешь изучить Moose если тебе нужен хардкор. Там многое из того, что тебе хочется уже сделали. Плата за это будет определенные «тормоза».

gh0stwizard ★★★★★ ()
Ответ на: комментарий от gh0stwizard

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

Который потом никто прочитать не сможет.

zloelamo ★★★★ ()
Ответ на: комментарий от zloelamo

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

Как говорил Линус, хороший программист думает о структурах, плохой о том как выглядит его код.

gh0stwizard ★★★★★ ()
Ответ на: комментарий от gh0stwizard

Как говорил Линус, хороший программист думает о структурах, плохой о том как выглядит его код.

Ты меня затролел!!!

В любом случае спасибо всем за предложеные варианты.

zloelamo ★★★★ ()
Ответ на: комментарий от zloelamo

И последнее. Хороший модуль, библиотека и т.д. это такой код, в который никто не заглядывает. Читаешь спецификацию, используешь по назначению и радуешься. Так что, если кто-то решится взглянуть на твой код, чтобы что-то исправить в нем это твоя вина. А то как внутри все будет выглядеть дело десятое, так как уже ясно, что код неидеальный ;-)

Пиши код, а рефакторинг, исправление багов, расставление подробных комментариев это неизбежность.

P.S. Все, больше досаждать философией тебя не буду. Со временем все придет само.

gh0stwizard ★★★★★ ()
Последнее исправление: gh0stwizard (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.