将所有值乘以%hash并返回具有相同结构的%hash

kaw*_*aii 6 rakudo perl6

我在数据库列中存储了一些JSON,如下所示:

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

作为“生成”算法的一部分,我正在尝试将此值计入%hash,将其乘以(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;
}
Run Code Online (Sandbox Code Playgroud)

实际上,这调用了我的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;
}
Run Code Online (Sandbox Code Playgroud)

但是我在执行时遇到以下错误(我想是因为我试图将整个哈希而不是哈希的单个元素进行多重处理):

$ 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
Run Code Online (Sandbox Code Playgroud)

有没有一种更简单(且可行的)方式来做到这一点,而无需为每个元素复制我的代码?:)

Tak*_*kao 6

Firstly, there is an issue with logic of your code. Initially, you are getting a hash of values, "feet": "6'07\"", "meters": 2.0 parsed out of json, with meters being a number and feet being a string. Next, you are trying to multiply it on a random value... And while it will work for a number, it won't for a string. Perl 6 allomorphs allow you to do that, actually: say "5" * 3 will return 15, but X"Y' pattern is complex enough for Perl 6 to not naturally understand it.

So you likely need to convert it before processing, and to convert it back afterwards.

The second thing is exact line that leads to the error you are observing.

Consider this:

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
Run Code Online (Sandbox Code Playgroud)

To work directly on hash values, you want to use map method and process every pair, for example: %a .= map({ .key => .value * (0.9..1.1).rand }).

Of course, it can be golfed or written in another manner, but the main issue is resolved this way.

  • 或使用metaoperator更改值:`%a &gt;&gt; * = &gt;&gt;(0.9 .. 1.1).rand (3认同)

rai*_*iph 5

您已经接受@Takao的回答。该解决方案需要手动挖掘%hash以获取叶哈希值/列表,然后应用map

鉴于您的问题的标题提到“返回... 相同的 结构 ”,并且主体包括看起来像嵌套结构的东西,我认为重要的是,有一个答案提供了一些惯用的解决方案,以自动降级并复制嵌套结构:

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 }
Run Code Online (Sandbox Code Playgroud)

Hyperops(例如»)迭代一个或两个数据结构以到达其叶子,然后应用被Hypered的op:

say %hash».++ # in-place increment leaf values of `%hash` even if nested
Run Code Online (Sandbox Code Playgroud)

.&{ ... }使用方法调用语法大括号内调用闭包。结合使用hyperop可以编写:

%hash».&{ $_ = (0.9 .. 1.1) .rand }
Run Code Online (Sandbox Code Playgroud)

另一种选择是.deepmap

%hash.deepmap: { $_ = (0.9..1.1).rand }
Run Code Online (Sandbox Code Playgroud)

hyperops和deepmap之间的主要区别在于,允许编译器迭代数据结构并以任何顺序并行运行超级操作,而deepmap迭代始终按顺序进行。