我在数据库列中存储了一些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
有没有一种更简单(和工作)的方法可以做到这一点,而无需为每个元素复制我的代码? :)
首先,代码的逻辑存在问题。最初,您获得的值哈希,"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 })
。
当然,它可以打高尔夫球或以其他方式编写,但主要问题是通过这种方式解决的。
你已经接受了@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
而迭代总是按顺序进行。