Последнее в категории Стиль кода

Hello, Perl 5--

| Комментариев: 2

Сегодня открыл свой CGI-скрипт 2002 года, который до сих пор, кстати, работает, сто раз дописанный и истерзанный, но выполненный за это время 105 миллионов раз. Хочу показать тот фан, на который сегодня смотреть смешно, но девять лет назад приходилось с трудом выцарапывать из книг и интернетов.

Ввод-вывод

binmode STDIN;
binmode STDOUT;

Глобальные переменные

our $dbh;
our $sth;

Дата и время

Разбор дат после чтения из базы данных.

my ($year, $month, $day, $hour, $min, $sec) = $date =~ /^(\d{4})-?(\d\d)-?(\d\d) ?(\d\d):?(\d\d):?(\d\d)/;

Дорисовка ведущего нуля.

$mday = "0$mday" if $mday =~ /^\d$/;
$hour = "0$hour" if $hour =~ /^\d$/;
$min = "0$min" if $min =~ /^\d$/;
$sec = "0$sec" if $sec =~ /^\d$/;
return "$days[$wday], $mday-$mons[$mon]-$year $hour:$min:$sec GMT";

Сохранение даты в хеше.

%datetime = ();
($datetime{"sec"}, $datetime{"min"}, $datetime{"hour"},
 $datetime{"day"}, $datetime{"month"}, $datetime{"year"},
 $datetime{"wday"}, $datetime{"yday"}, $datetime{"isdst"}) = localtime (time);
$datetime{'month'}++;
$datetime{'year'} += 1900;

Работа со строками

Декодирование строки запроса.

sub _urldecode{
 local($val)=@_;
 $val=~s/\+/ /g;
 $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/ge;
 return $val;
}

Отдельный фетиш — ручное декодирование UTF. В частности, есть такая таблица. И еще пара нетривиальных функций, которые я здесь не привожу.

sub init_utftable{
%utftable = (
  0xD020   => 0x20,
  0xD082   => 0x80,
  0xD083   => 0x81,
  0xE2809A => 0x82,
  0xD193   => 0x83,
  0xE2809E => 0x84,
  . . .
  0xD18D   => 0xFD,
  0xD18E   => 0xFE,
  0xD18F   => 0xFF);
}

Смена регистра.

sub lower_case{
  my $word = shift;
  $word =~ tr /АБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ/ абвгдеёжзиклмнопрстуфхцчшщъыьэюя/;
  return lc $word;
}

sub upper_case{
  my $word = shift;
  $word =~ tr /абвгдеёжзиклмнопрстуфхцчшщъыьэюя/ АБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ/;
  return lc $word;
}

О кавычках

| Нет комментариев

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

С одной стороны, если всегда писать двойные кавычки, то не придется судорожно отлаживать изменения, когда дописываешь к строке переменную или перевод строки, а получаешь код $var\n на выводе.

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

Написал «безопасный» SQL-запрос:

my $sth = $dbh->prepare("
    update
        thread
    set
        title = ?
    where
        $thread_id = ?
");
$sth->execute($title, $thread_id);

То, что поле в таблице называется id, а не thread_id, не важно: MySQL не сообщила об ошибке, а спокойно проинтерпретировала интерполированную строку с подставленным значением "...where 8899 = 8899", обновив все заголовки во всей таблице. 

({}) vs. ()

| 1 комментарий

Сколько существует YAPC::TV, столько существует и небольшой вспомогательный скрипт, который автоматизирует работу по конвертированию видеофайлов в разные форматы.

В этой программе есть функция convert, которая принимает ссылку на хеш:

sub convert {
    my $param = shift;

И затем разыменовывает ее:

my $s = "-s $$param{s}" if $$param{s};

При этом каждый вызов convert содержит обилие скобок — одни для вызова функции, другие для создания анонимного хеша:

convert({
    type => 'mpg',
    s => '480x270',
    r => 25,
    b => '512kb',
    ar => 22050,
});

Конечно, можно было бы вторые скобки не ставить вообще, а в функции принимать готовый хеш:

sub convert {
    my %param = @_;

Но тут возникает вопрос о том, как этому научить начинающего программиста. Конструкции вида %hash = @array не так просто понять в начале изучения языка, как это кажется после нескольких лет использования перла.

Хотя различие между следующими двумя строками схватывается на ура:

$a = @a;
($a) = @a;

Время от времени в интернетах возникает вопрос о том, как спрятать исходники при передаче заказчику проекта на перле. Известно, что эта задача не имеет решения.

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

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

Другой пример — требуется предоставить демонстрационную версию программы.

Простое решение, для которого даже не потребуется установки никаких дополнительных утилит, — воспользоваться препроцессором С. Компилятор gcc не в счет: он наверняка уже установлен в системе.

package My::Module;
. . .
#ifdef DEMO_MODE
say rand(10);
#else
say $this->real_method();
#endif
. . .

Еще один пример предложенного подхода — эмуляция встроенной функции say, если потребуется развернуть приложение, в котором из возможностей версии 5.10 используется только say, на сервере со старым перлом:

#ifndef SAY
sub say {
   print @_, "\n";
}
#endif

Все, что требуется для того, чтобы на основании таких заготовок получить код для передачи, — запустить препроцессор C:

$ gcc -DDEMO_MODE -E mymodule.pm.c > mymodule.pm

Точка с запятой в перле необязательна, если она приходится на конец блока:

sub doit {
    say 123
}

В отличие от, например, C++, где аналогичная конструкция вызовет ошибку компиляции:

int main() {
   return 0
}

s.cpp: In function 'int main()':
s.cpp:3: error: expected ';' before '}' token

Различие здесь в том, чем точка с запятой является с точки зрения языка. В перле это разделитель инструкций, в C++ — признак окончания выражения.

В JavaScript ситуация еще интереснее: там точки с запятой допустимо опускать не только в конце блока, но и в конце строки:

x += id.value
alert(x)

При этом разрешено продолжать выражение на следующей строке:

x +=
   id.value
alert(x)

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

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

В перле иногда очень соблазнительно не только опускать точку с запятой в конце блока, но и ключевое слово return в последней инструкции подпрограммы:

sub add {
    my ($a, $b) = @_;
    $a + $b
}

Результат выражения $a + $b — последнее значение, вычисленное в теле подпрограммы — считается результатом, который и нужно передать вызывающему коду.

Выглядит лаконично, не спорю. Но такая экономия на символах обычно оборачивается недоумением при попытках внести в код изменения.

Ситуация очень похожа на рекомендацию ставить запятую после последнего элемента при объявлении хеша:

my %x = (
    alpha => 'a',
    beta => 'b',
);

Перл создает в хеше ровно два элемента, но запятая помогает при добавлении новых элементов (которые часто добавляются копированием и вставкой последней строки). Этот фокус не проходит с яваскриптом: если поставить запятую после последнего элемента массива, то будет создан еще один элемент со значением undefined.

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

sub add {
    my ($a, $b) = @_;
    $a + $b
    warn $a;
}

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

sub add {
    my ($a, $b) = @_;
    $a + $b
;    warn $a;
}

Такие трюки с пропущенной запятой — один из приемов, которые малыми шажками ведут код к read-only-модели.

Например, продолжая такую идеологию возможно было бы записать функцию так:

sub add {
    shift() + shift
}

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

Страницы

  • img

Об архиве

Эта страница содержит последние записи категории Стиль кода.

Предыдущая категория — Сообщество.

Следующая категория — Тесты.

Смотрите новые записи на главной странице или загляните в архив, где есть ссылки на все сообщения.