Прототипирование подпрограммы Perl - правильный способ сделать это - PullRequest
7 голосов
/ 15 ноября 2011

У меня есть подпрограмма с именем debug, которую я использую в своем коде. Это в основном позволяет мне видеть, что происходит и т. Д.

sub debug {
    my $message      = shift;
    my $messageLevel = shift;

    our $debugLevel;
    $messageLevel = 1 if not defined $messageLevel;
    return if $messageLevel > $debugLevel;
    my $printMessage = "    " x $messageLevel . "DEBUG: $message\n";
    print STDERR $printMessage;
    return $printMessage;
}

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

debug "Here I am! And the value of foo is $foo";

или

debug "I am in subroutine foo", 3;

В то же время мне нравится помещать определения подпрограмм внизу моей программы, так что вам не нужно переходить на 1/2 пути по коду, чтобы найти суть программы.

Я бы хотел сделать это:

sub debug($;$);  #Prototype debug subroutine

/Here goes the main program code/

sub debug {      #The entire subroutine goes here
   /Here goes the debug subroutine code/
}

Однако, когда я делаю это, я получаю предупреждение:

Prototype mismatch: sub main::debug ($;$) vs none at foo.pl line 249.

Итак, я застрял, поместив определение прототипа в обоих местах. Как правильно сделать что-то подобное?


РЕАКЦИЯ

Стоп! Модульное время. - Крис Латс

Модуль? Вы имеете в виду создать отдельный файл? Это добавляет некоторые сложности без решения проблемы, которую я пытаюсь решить: устранение необходимости в скобках вокруг этой конкретной подпрограммы.

our $ debugLevel; в любом случае не должно быть в теле, но я согласен с Крисом в этом. - Синан Юнур 3 часа назад

В этом случае our $debugLevel не обязательно должно быть, но если я определил класс и хочу использовать эту подпрограмму в своем классе для отладки, он мне нужен. Я могу поставить его в своем классе как ::debug

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

Я надеялся на легкий способ избежать этого. Эрик Стром показал, что есть способ. К сожалению, это дольше, чем моя debug рутина.

Раньше я использовал прототипы, но у меня появилась привычка не писать отдельных объявлений для подпрограмм и использовать скобки для всех вызовов: debug («Я в подпрограмме foo», 3) ;. Было высказано предположение, что прототипы действительно не очень хорошая идея. TMTOWTDI - Кит Томпсон 3 часа

За исключением того, что я склонен делать:

debug (qq(The value of Foo is "$foo"), 3);

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

Зачем вам прототипы? См. Этот вопрос Как передать необязательные параметры в функцию Perl - TLP

Да, есть много проблем с прототипированием. Основная проблема в том, что он просто не делает то, что, как думают люди, должен делать: объявляет типы переменных для параметров, передаваемых в вашу функцию.

Это не причина, по которой я здесь использую прототипы.

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

Ответы [ 5 ]

26 голосов
/ 15 ноября 2011

Просто избавьтесь от прототипов в целом:

sub debug;

debug "Here I am! And the value of foo is $foo";
debug "I am in subroutine foo", 3;

sub debug {
    # body of debug
}
6 голосов
/ 15 ноября 2011

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

sub debug ($;$);

debug 'foo';

use Scalar::Util 'set_prototype';
sub install {
    my ($name, $code) = @_;
    my $glob = do {no strict 'refs'; \*$name};
    set_prototype \&$code, prototype \&$glob;
    *$glob = $code;
}

BEGIN {
    install debug => sub {
        print "body of debug: @_\n";
    };
}

install - это просто оболочка для функции Scalar::Util set_prototype, которая позволяет вам изменять прототип coderef после его создания.

Прототипы могут быть очень полезны, но при использовании скалярного прототипа всегда спрашивайте себя, действительно ли это то, что вы хотели. Поскольку прототип ($;$) сообщает perl «отладка - это функция, которая может принимать один или два аргумента, каждый со скалярным контекстом, наложенным на сайт вызова».

Немного о контексте - то, где люди обычно запутываются, потому что тогда, если вы попытаетесь сделать это:

my @array = qw(one two);

debug @array;

Затем @array становится видимым в скалярном контексте и становится 2. Таким образом, вызов становится debug 2;, а не debug 'one', 'two';, как вы могли ожидать.

3 голосов
/ 15 ноября 2011

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

Например, этот код работает правильно:

#!/usr/bin/env perl

use strict;
use warnings;

use subs qw/mysay/;

mysay "Yo";
mysay "Yo", "Joel";

sub mysay ($;$) {
  my $message = shift;
  my $speaker = shift;
  if (defined $speaker) {
    $message = "$speaker says: " . $message;
  }
  print $message, "\n";
}
1 голос
/ 15 ноября 2011

Вы должны объявить тот же прототип при определении подпрограммы:

sub debug($;$); # prototype/declare

... meat of the program ...

sub debug($;$) {
    ...
}
0 голосов
/ 04 июня 2012

Вот как это сделать:

sub debug;  #Prototype debug subroutine

#Here goes the main program code/

sub debug($;$) {
   #Here goes the debug subroutine code/
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...