我在数据库列中存储了一些JSON,如下所示:
pokeapi=# SELECT height FROM pokeapi_pokedex WHERE species = 'Ninetales';
-[ RECORD 1 ]------------------------------------------
height | {"default": {"feet": "6'07\"", "meters": 2.0}}
作为我正在研究的“生成”算法的一部分,我想将此值计入%hash,然后将其乘以(0.9..1.1).rand
(以允许高度自然变化10% ),然后在相同的结构中创建一个新的%hash。我的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
是否存在一种更简便(且可行的)方式来做到这一点,而无需为每个元素重复我的代码? :)
答案 0 :(得分:6)
首先,您的代码逻辑存在问题。最初,您会得到一个值散列,"feet": "6'07\"", "meters": 2.0
是从json中解析出来的,其中meters
是一个数字,而feet
是一个字符串。接下来,您尝试将其乘以一个随机值...虽然它适用于数字,但不适用于字符串。 Perl 6 allomorphs允许您这样做,实际上: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 })
。
当然,它可以打高尔夫球或用其他方式书写,但是主要问题可以通过这种方式解决。
答案 1 :(得分: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 }
Hyperops(例如»
)迭代一个或两个数据结构到达其叶子,然后应用要被超载的操作:
say %hash».++ # in-place increment leaf values of `%hash` even if nested
.&{ ... }
使用方法调用语法将大括号括起来。结合使用hyperop可以编写:
%hash».&{ $_ = (0.9 .. 1.1) .rand }
另一个选项是.deepmap
:
%hash.deepmap: { $_ = (0.9..1.1).rand }
hyperops与deepmap之间的主要区别在于,允许编译器以任何顺序迭代数据结构并并行运行超级操作,而deepmap
迭代始终按顺序进行。