Все задачи

Тотальная нумерификация

06 Apr 2014

Памятуя о проблемах с числами, которые на самом деле строки, программист написал фукнцию, которая рекурсивно обходит переданную ей структуру данных и in-place нумерифицирует строки, похожие на числа.

use Scalar::Util qw(looks_like_number);

# было  {"1":"c","3":["1","b",3],"d":"5"}
# стало {"1":"c","3":[1,"b",3],"d":5}
# ключи хешей не трогаем -- в json они всегда строки
# можно пользоваться так: print to_json(numerify($data));
sub numerify
{
    my $data = shift;

    if (ref $data eq ""){
        if ( looks_like_number($data) ){
            $data += 0;
        }
    } elsif ( ref $data eq 'ARRAY' ){
        numerify($_) for @$data;
    } elsif ( ref $data eq 'HASH' ) {
        numerify($_) for values %$data;
    }

    return $data;
}

Но что-то тут не заладилось…

Подсказка

Показать

print to_json(numerify({1=>'c', 3=> ['1','b',3], d=>"5"}));
{"1":"c","3":["1","b",3],"d":"5"}

Выглядит так, будто нумерификация вообще не работает.

Подсказка-2

Показать

Any arguments passed in show up in the array @_. (...) The array @_ is a local array, but its elements are aliases for the actual scalar parameters. (...) Because the assignment copies the values, this also has the effect of turning call-by-reference into call-by-value. perldoc perlsub
Любые аргументы, передаваемые в подпрограму Perl, поступают в нее в виде массива @_. (...) Массив @_ является локальным, но его значения служат псевдонимами для реальных скалярных параметров. (...) Копирование значений заменяем семантику передачи по ссылке семантикой передачи по значению.

Разоблачение

Показать

Проблема в строчке

    my $data = shift;

Здесь создается локальная для функции копия входного параметра, и когда рекурсия спускается до скалярных значений, нумерификация путем сложением с 0 производится именно над локальной копией скаляра, не затрагивая исходное значение.

Чтобы действительно поменять переданную структуру, стоит работать непосредственно с элементами массива @_:

sub numerify_good
{ 
    if (ref $_[0] eq ""){
        if ( looks_like_number($_[0]) ){
            $_[0] += 0;
        } 
    } elsif ( ref $_[0] eq 'ARRAY' ){
        numerify_good($_) for @{$_[0]};
    } elsif ( ref $_[0] eq 'HASH' ) {
        numerify_good($_) for values %{$_[0]};
    } 

    return $_[0];
} 

Самое точное и полное объяснение проблемы прислал наш читатель Роман Луговкин. Здорово!

Еще на тему кавычек в json: топик на StackOverflow