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)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.