Умножьте все значения в% хеш и верните% хеш с той же структурой - PullRequest
6 голосов
/ 18 апреля 2019

У меня есть некоторый JSON, хранящийся в столбце базы данных, который выглядит следующим образом:

pokeapi=# SELECT height FROM pokeapi_pokedex WHERE species = 'Ninetales';
-[ RECORD 1 ]------------------------------------------
height | {"default": {"feet": "6'07\"", "meters": 2.0}}

Как часть алгоритма «генерации», над которым я работаю, я бы хотел перенести это значение в%хэш, умножьте его на (0.9..1.1).rand (чтобы учесть естественную 10% -ную дисперсию в высоте), а затем создайте новый хеш% в той же структуре.Мой select-height метод выглядит следующим образом:

method select-height(:$species, :$form = 'default') {
    my %heights = $.data-source.get-height(:$species, :$form);

    my %height = %heights * (0.9..1.1).rand;

    say %height;
}

, который фактически вызывает мою процедуру get-height, чтобы получить «средние» высоты (как в метрических, так и в имперских) для этого вида.

method get-height (:$species, :$form) {
    my $query = dbh.prepare(qq:to/STATEMENT/);
           SELECT height FROM pokeapi_pokedex WHERE species = ?;
        STATEMENT

    $query.execute($species);

    my %height = from-json($query.row);
    my %heights = self.values-or-defaults(%height, $form);

    return %heights;
}

Однако при выполнении я получил следующую ошибку (я полагаю, потому что я пытаюсь умножить хэш в целом, а не отдельные элементы хэша):

$ perl6 -I lib/ examples/height-weight.p6
{feet => 6'07", meters => 2}
Odd number of elements found where hash initializer expected:
Only saw: 1.8693857987465123e0
  in method select-height at /home/kane/Projects/kawaii/p6-pokeapi/lib/Pokeapi/Pokemon/Generator.pm6 (Pokeapi::Pokemon::Generator) line 22
  in block <unit> at examples/height-weight.p6 line 7

Есть либолее простой (и рабочий) способ сделать это без дублирования моего кода для каждого элемента?:)

Ответы [ 2 ]

6 голосов
/ 18 апреля 2019

Во-первых, существует проблема с логикой вашего кода. Изначально вы получаете хэш значений, "feet": "6'07\"", "meters": 2.0 анализируется из json, meters - число, а feet - строка. Затем вы пытаетесь умножить его на случайное значение ... И хотя оно будет работать для числа, оно не будет для строки. Алломорфы Perl 6 позволяют вам сделать это, на самом деле: say "5" * 3 вернет 15, но шаблон X"Y' достаточно сложен для Perl 6, чтобы не понимать его естественным образом.

Таким образом, вам, вероятно, потребуется преобразовать его перед обработкой, а затем преобразовать обратно.

Второе - это точная линия, которая ведет к ошибке, которую вы наблюдаете.

Учтите это:

my %a = a => 5;
%a = %a * 10 => 5; # %a becomes a hash with a single value of 10 => 5
# It happens because when a Hash is used in math ops, its size is used as a value
# Thus, if you have a single value, it'll become 1 * 10, thus 10
# And for %a = a => 1, b => 2; %a * 5 will be evaluated to 10
%a = %a * 10; # error, the key is passed, but not a value

Чтобы работать непосредственно с хэш-значениями, вы хотите использовать метод map и обрабатывать каждую пару, например: %a .= map({ .key => .value * (0.9..1.1).rand }).

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

5 голосов
/ 19 апреля 2019

Вы приняли ответ @ Такао.Это решение требует ручного поиска в %hash, чтобы перейти к листовым хешам / спискам, а затем применения map.

, учитывая, что заголовок вашего вопроса упоминает "return ... same структура ", а тело включает в себя то, что выглядит как вложенная структура, я думаю, что важно, что есть ответ, предоставляющий некоторые идиоматические решения для автоматического спуска и дублирования вложенной структуры:

my %hash = :a{:b{:c,:d}}

say my %new-hash = %hash».&{ (0.9 .. 1.1) .rand }
# {a => {b => {c => 1.0476391741359872, d => 0.963626602773474}}}

# Update leaf values of original `%hash` in-place:
%hash».&{ $_ = (0.9 .. 1.1) .rand }

# Same effect:
%hash »*=» (0.9..1.1).rand;

# Same effect:
%hash.deepmap: { $_ = (0.9..1.1).rand }

Гиперопс (например, ») итерируют одну или две структуры данных, чтобы добраться до их листьев, а затем применяют операцию с гиперссылкой:

say %hash».++ # in-place increment leaf values of `%hash` even if nested

.&{ ... } вызывает замыкание в фигурных скобках с использованием синтаксиса вызова метода.Комбинируя это с гиперобъемом, можно написать:

%hash».&{ $_ = (0.9 .. 1.1) .rand }

Другой вариант: .deepmap:

%hash.deepmap: { $_ = (0.9..1.1).rand }

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

...