О вреде переопределения оператора '""'

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

Известно где работал вот такой код, который по географическим координатам двух точек на Земле определял расстояние между ними:

my $gis = new GIS::Distance;
my $distance = $gis->distance(@coords_pair);
. . .
add_node{$node, 'pair', {
   . . .
   distance => int($distance + 0.5),
});

В переменной $distance — вычисленное расстояние в километрах, которое округляется и передается функции add_node, чтобы построить XML-узел, вписав расстояние в одноименный атрибут.

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

my $fraction = $distance / 40_075.696;
add_node{$node, 'pair', {
   . . .
   fraction => $fraction,
   distance => int($distance + 0.5),
});

Посмотрел на результат, и вижу, что расстояние между Питером и Пекином — 15% длины экватора. Отлично. Но что такое?! — и само расстояние стало 15 километров.

Так и появился бы еще один седой волос, но warn ref $distance легко ответило на вопрос «какого?»:

Class::Measure::Length at /home/ash/. . ./Place/Worker.pm line 493.

Переменная $distance — ссылка на объект типа Class::Measure::Length; такого же типа становится и переменная $reference. Фактически обе переменные указывают на одно и то же. Когда я делю расстояние на длину экватора и сохраняю результат в $reference, я тем самым порчу значение, которое — как я думал — содержится в переменной $distance.

Оказалось, что Class::Measure, от которого унаследован Class::Measure::Length, одним из первых действий переопределяет пять операторов:

use overload
    '+'=>\&_ol_add, '-'=>\&_ol_sub,
    '*'=>\&_ol_mult, '/'=>\&_ol_div,
    '""'=>\&_ol_str;

Как бы я ни пользовался переменной $distance — пытался бы интерполировать его в строке или использовал бы в арифметических выражениях, всегда инициировались переопределенные операторы.

Смотрим на поведение на изолированном примере:

use v5.10;
use strict;

use Class::Measure::Length;

my $m = new Class::Measure::Length(1, 'inch');
my $f = int($m + 0.5);
say $m;

Здесь последовательно вызываются три метода:

_ol_add
_ol_str
_ol_str

Первые два приходятся на строку

my $f = int($m + 0.5);

Сначала вычисляется сумма _ol_add($m, 0.5), а затем результат (типа Class::Measure::Length) стрингифицируется.

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

my $distance = $gis->distance(@coords_pair)->value;

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

Комментировать

Страницы

  • img

Об этой записи

Сообщение опубликовано 21.12.2009 10:04. Автор — ash.

Предыдущая запись — Вылетаем на Saint Perl

Следующая запись — Вызов Perl из XSLT

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