RakuHTTPクライアントリクエストからJSONを抽出する

Nov 26 2020

このRakuコードの何が問題なのか理解できません。

WebサイトからJSONをフェッチし、JSON内の配列の各アイテムからフィールドを印刷したいと思います(この場合、Discourseフォーラムの最新トピックのタイトル)。

これは私が動作することを期待していたコードですが、失敗しました:

use HTTP::UserAgent;
use JSON::Tiny;

my $client = HTTP::UserAgent.new; $client.timeout = 10;

my $url = 'https://meta.discourse.org/latest.json'; my $resp = $client.get($url);

my %data = from-json($resp.content); # I think the problem starts here. my @topics = %data<topic_list><topics>; say @topics.WHAT; #=> (Array) for @topics -> $topic {
    say $topic<fancy_title>;
}

エラーメッセージは次のsay $topic<fancy_title>行からのものです。

Type Array does not support associative indexing.
  in block <unit> at http-clients/http.raku line 18

これはハッシュの配列であるため、$topicと書く必要があると思って%topicいましたが、これは機能しません。

for @topics -> %topic {
    say %topic<fancy_title>;
}

そのためのエラーメッセージは次のとおりです。

Type check failed in binding to parameter '%topic'; expected Associative but got Array ([{:archetype("regula...)
  in block <unit> at http-clients/http.raku line 17

データを検査する場合、それは配列ではなくハッシュである必要があります。試しました@arrayが、正しくないことがわかったので、に変更%topicしました$topic

.list定義する行に追加することでようやく動作@topicsするようになりました@topicsが、(Array)それが追加されるかどうかであるため、なぜそれが修正されるのかわかりません。

これは作業コードです:

use HTTP::UserAgent;
use JSON::Tiny;

my $client = HTTP::UserAgent.new; $client.timeout = 10;

my $url = 'https://meta.discourse.org/latest.json'; my $resp = $client.get($url);

my %data = from-json($resp.content); # Adding `.list` here makes it work, but the type doesn't change. # Why is `.list` needed? my @topics = %data<topic_list><topics>.list; say @topics.WHAT; #=> (Array) # Why is it `$topic` instead of `%topic`?
for @topics -> $topic { say $topic<fancy_title>;
}

なぜ失敗するのか、そしてこのタスクを実行する正しい方法を誰かが知っていますか?

回答

8 user0721090601 Nov 26 2020 at 07:06

何が起こったのかというと、あなたが言うときに配列の配列を作成したということです

my @topics = %data<topic_list><topics>;

これはこれらのモジュールに固有のものではありませんが、配列が割り当てられたRaku全体で一般的です。

何が起こっているのかを確認するために、より単純なハッシュを使用してみましょう。

my %x = y => [1,2,3];

my $b = %x<y>; my @b = %x<y>; say $b;  # [1 2 3]
say @b;  # [[1 2 3]]

キャッチは、配列割り当て(変数に@印章がある場合に使用される)がスカラーコンテナー内にあるので%x<y> 単一のアイテムとして解釈され、それが喜んで入れられること@b[0]です。あなたはモジュール自体をコントロールすることはできませんが、あなたが言うならば、あなたは私の例で違いを見ることができるmy %x is Map = …ようにMapスカラーコンテナ内のアイテムを配置しませんが、Hashオブジェクトが行います。Rakuに、単一のコンテナではなく、単一のアイテムをそのコンテンツとして扱うように指示する方法は2つあります。

  • 配列をバインドするを使用する
    代わりに@b = %x<y>、を使用します@b := %x<y>@-sigiled変数へのバインドは、自動的にコンテナー化解除されます。
  • zen演算子を使用する
    リスト/ハッシュ値が1つとして扱われる可能性を回避したい場合は、zenスライスを使用して、コンテナーが存在する場合はそれを削除できます。これは、行うことができるいずれか(割り当て時@b = %x<y>[]またはforループ(for @b[] -> $b)。、、およびは、実際のタイプに関係なく、事実上同義語であること<>に注意してください。ほとんどの人は、前のものと一致するものを使用するだけです。[]{}

したがって、コードでは、次のことを実行できます。

...
my %data = from-json($resp.content);

my @topics := %data<topic_list><topics>;   # (option 1) binding
my @topics  = %data<topic_list><topics><>; # (option 2) zen slice

for @topics -> $topic { say $topic<fancy_title>;
}

または、オプション3として、ループ内で。

for @topics<> -> $topic { say $topic<fancy_title>;
}

.list修正が行われる理由は、残りの回答の後で推測できるように、コンテナーにない新しいリストを返すためです。