先日の JSONata の README.md を訳してみた をやってみて JSONata の面白さに気がつきました。あと この説明ページ もお勧め。これ、発展していけば JSON 処理の基本ライブラリとして、文字列操作の正規表現(RegEx)のように活用されるんじゃない?と期待してしまいます。
そこで今回は、公式サイトの documentation にある、文法ガイド をざっと訳してみました。ちと長かったのですが、土日にキアイで!これ読んで、皆さんも JSONata 始めませんか?
Overview
元のページはこちら: Overview / JSONata
前書き
JSONataは、JSONデータの軽量クエリおよび変換言語です。
XPath 3.1 の 'location path' セマンティクスに触発されているため、洗練されたクエリをコンパクトで直観的な表記法で表現できます。
豊富な組み込み演算子や関数が提供されており、抽出されたデータを操作および結合できます。そしてそれを使い慣れた JSON オブジェクトおよび配列構文を使用して任意の JSON 形式にフォーマットして出力できます。
更にユーザー定義関数を作成する機能と組み合わせることで、JSON の問合せおよび変換タスクを実行する高度な式を構築できるでしょう。
説明ビデオ
以下の5分間のビデオを鑑賞し、JSONata の概要を理解しましょう。
【訳注】英語がわからなくても画面を見ているだけで、ほぼ理解できるとおもいます!
JSONata を入手する
- try.jsonata.org で試してみてください
- JavaScript モジュール を入手する
言語ガイド
基本選択
元のページはこちら: Language guide / Basic Selection
JSON 構造からの値の抽出をサポートするために、ロケーションパス構文が定義されています。XPath と同様に、指定されたロケーションパスに一致するドキュメント内のすべての値が選択されます。JSON の基本構造はオブジェクトと配列の2つです。
JSON オブジェクトの操作
JSON オブジェクトは連想配列です (マップまたはハッシュマップ)。任意に深くネストされた JSON オブジェクトの構造をナビゲートするためのロケーションパス構文は、 .
の区切り文字で区切られたフィールド名で構成されています。
この式は、ロケーションパスの最後のステップにナビゲートした後に参照される JSON 値を返します。ロケーションパスのナビゲーション中にフィールドが見つからない場合、式は何も返しません (Javascript では undefined
です)。入力文書に対象となるデータが存在しない場合でも、エラーはスローされません。
以下のサンプル JSON ドキュメントは、特に記載がない限り、このガイドの例で使用されています:
{
"FirstName": "Fred",
"Surname": "Smith",
"Age": 28,
"Address": {
"Street": "Hursley Park",
"City": "Winchester",
"Postcode": "SO21 2JN"
},
"Phone": [
{
"type": "home",
"number": "0203 544 1234"
},
{
"type": "office",
"number": "01962 001234"
},
{
"type": "office",
"number": "01962 001235"
},
{
"type": "mobile",
"number": "077 7700 1234"
}
],
"Email": [
{
"type": "work",
"address": ["fred.smith@my-work.com", "fsmith@my-work.com"]
},
{
"type": "home",
"address": ["freddy@my-social.com", "frederic.smith@very-serious.com"]
}
],
"Other": {
"Over 18 ?": true,
"Misc": null,
"Alternative.Address": {
"Street": "Brick Lane",
"City": "London",
"Postcode": "E1 6RF"
}
}
}
以下のような Expression をこの JSON ドキュメントに適用すると、次の結果をもたらします:
Expression | 結果 | コメント |
---|---|---|
Surname | "Smith" | JSON 文字列を返す (ダブルクォーテーション文字で囲まれている) |
Age | 28 | JSON 数値を返す |
Address.City | "Winchester" | フィールド参照は ‘.’ で区切られる |
Other.Misc | null | パスにマッチして null 値を返す |
Other.Nothing | パスが見つからず、Javascript の undefined を返す | |
Other.`Over 18 ?` | true | 空白または予約トークンを含むフィールド参照はバッククォートで囲む |
JSON 配列の操作
JSON 配列は、順序付けられた値のコレクションが必要な場合に使用されます。配列内の各値は、名前ではなくインデックス(位置)に関連付けられているため、配列内の個々の値を扱うためには、インデックスを指定するための特別な構文が必要です。これは、配列のフィールド名の後に角括弧 []
を使用して行います。
角括弧 []
に数値または数値に評価される式が含まれる場合、数値は選択する値のインデックスを表します。インデックスはゼロから始まり、配列 arr
の最初の値は arr[0]
です。数値が整数でない場合は、整数に切り捨てられます。角括弧 []
内の式が数値でないか、または数値に評価されない式である場合、それは 述語(predicates) として扱われます。
負のインデックスは、配列の最後から数えます。たとえば、arr[-1]
は最後の値を、arr[-2]
は最後から2番目のものを選択します。インデックスが配列のサイズを超えて指定された場合、何も選択されません。
配列にインデックスが指定されていない場合(つまり、フィールド参照の後に角括弧 []
がない場合)、配列全体が選択されます。配列にオブジェクトが含まれていて、ロケーションパスがこれらのオブジェクト内のフィールドを選択すると、配列内の各オブジェクトが選択されるようにクエリされます。
E x p r e s s i o n | 結 果 | コメント |
---|---|---|
Phone[0] | { "type": "home", "number": "0203 544 1234" } | 最初の要素(オブジェクト)を返す |
Phone[1] | { "type": "office", "number": "01962 001234" } | 2番目の要素を返す |
Phone[-1] | { "type": "mobile", "number": "077 7700 1234" } | 最後の要素を返す |
Phone[-2] | { "type": "office", "number": "01962 001235" } | 最後から2番目の要素を返す |
Phone[8] | 存在しないので nothing を返す | |
Phone[0].number | "0203 544 1234" | 最初の要素の number フィールドを選択 |
Phone.number | [ "0203 544 1234", "01962 001234", "01962 001235", "077 7700 1234" ] | Phoneにインデックスが与えられていないので、それらのすべて(配列全体)が選択され、それぞれの number フィールドをすべて選択 |
Phone.number[0] | [ "0203 544 1234", "01962 001234", "01962 001235", "077 7700 1234" ] | 最初の数字だけを返すと予想するかもしれませんが、Phone によって選択された各項目の最初の数字を返します |
(Phone.number)[0] | "0203 544 1234" | Phone.number によって返された配列にインデックスを適用します。括弧の利用法のひとつ |
Phone[[0..1]] | [{ "type": "home", "number": "0203 544 1234" },{ "type": "office", "number": "01962 001234" }] | インデックスの配列を作成して項目の範囲を返す |
トップレベル配列、ネストされた配列、配列の平坦化
JSON ドキュメントを考えてみましょう:
[
{ "ref": [ 1,2 ] },
{ "ref": [ 3,4 ] }
]
このドキュメントのトップレベルには、オブジェクトではなく配列があります。 この最上位の配列の最初のオブジェクトを選択する場合、[0]
を追加するフィールド名はありません。配列のコンストラクタの構文と衝突するので、[0]
を単独で使用することはできません。 しかし、コンテキスト参照 $
を使用して、ドキュメントの先頭を次のように参照することができます:
E x p r e s s i o n | 結 果 | コメント |
---|---|---|
$[0] | { "ref": [ 1,2 ] } | 式の先頭にある $ は、入力文書全体を参照する |
$[0].ref | [ 1,2 ] | .ref は内部配列全体を返す |
$[0].ref[0] | 1 | 内部配列の最初の位置にある要素を返す |
$.ref | [ 1, 2, 3, 4 ] | ネストされた配列の構造にもかかわらず、結果の選択はフラットな単一の配列に平坦化されます。入力配列の元の入れ子構造は失われます。 結果に元の構造を維持する方法については、Array コンストラクタ を参照してください。 |
複雑な選択
元のページはこちら: Language guide / Complex Selection
ワイルドカード
フィールド名の代わりに *
を使用して、オブジェクト内のすべてのフィールドを選択できる:
E x p r e s s i o n | 結 果 | コメント |
---|---|---|
Address.* | [ "Hursley Park", "Winchester", "SO21 2JN" ] | Address のすべてのフィールドの値を選択 |
*.Postcode | "SO21 2JN" | 任意の子オブジェクトの Postcode 値を選択 |
任意の深度をナビゲートする
*
の代わりに子孫ワイルドカード **
を用いると、すべての子孫を走査します(複数レベルのワイルドカード):
E x p r e s s i o n | 結 果 | コメント |
---|---|---|
**.Postcode | [ "SO21 2JN", "E1 6RF" ] | ネストされているかどうかにかかわらず、構造内のすべての Postcode 値を選択 |
述語 (Predicates)
ロケーションパスの任意のステップで、選択された項目は、expr
がブール値に評価される述語 - [expr]
を使用してフィルタリングできます。
選択項目の各項目は式に対してテストされ、真と評価された場合は項目が保持されます。falseの場合、選択範囲から削除されます。式は現在テストされている(コンテキスト)項目を基準にして評価されるため、述語式がナビゲーションを実行する場合、このコンテキスト項目に対し相対的に扱われます。
例:
E x p r e s s i o n | 結 果 | コメント |
---|---|---|
Phone[type='mobile'] | { "type": "mobile", "number": "077 7700 1234" } | type フィールドが "mobile" に等しい Phone 要素を選択 |
Phone[type='mobile'].number | "077 7700 1234" | 携帯電話(mobile phone)の number を選択 |
Phone[type='office'].number | [ "01962 001234", "01962 001235" ] | オフィス電話(office phone)の number を選択、2つあります! |
シングルトン配列と値の等価
JSONata 式または部分式の中では、値自体(配列自体ではない)とその値を含む配列は等価とみなされます。
これにより、配列から複数の値を抽出するオブジェクトパスと、オブジェクトから単一の値を抽出するロケーションパスの2つを、同一の構文で扱うことができ、互いに入力として使用することで自在に組み合わせることができます。
次の例を考えてみよう:
-
Address.City
は単一の値"Winchester"
を返す -
Phone[0].number
は単一の値とマッチし、その値"0203 544 1234"
を返す -
Phone[type='home'].number
も同様に単一の値"0203 544 1234"
とマッチする -
Phone[type='office'].number
は2つの値にマッチするので、配列[ "01962 001234", "01962 001235" ]
を返す
JSONata 式の戻り値を処理する場合、一致した値の数に関係なく、結果を一貫した形式にすることが望ましい場合があります。
上記の最初の2つの式では、各式が構造内の単一の値を扱っていることは明らかで、その値だけを返すことは意味があります。しかし、最後の2つの式では、一致する値の数がすぐに分かりません。返される内容によってホスト言語が異なる方法で結果を処理する必要がある場合、これではあまり助けになりません。
もしこれが問題であれば、単一の値だけがマッチしたとしても配列を返すように式を修正することができます。これは、空白の大括弧 []
をロケーションパス内のステップに追加することによって行われます。上記の例は、次のように常に配列を返すように書き直すことができます:
-
Address[].City
は[ "Winchester"]
を返す -
Phone[0][].number
は[ "0203 544 1234" ]
を返す -
Phone[][type='home'].number
は[ "0203 544 1234" ]
を返す -
Phone[type='office'].number[]
は[ "01962 001234", "01962 001235" ]
を返す
[]
は述語のどちらかの側に置くことができ、パス式の任意のステップに置くことができます。
値の結合
元のページはこちら: Language guide / Combining values
文字列式
文字列値を指すパス式はその値を返します。文字列は、連結演算子 &
を使用して結合できます。
例:
E x p r e s s i o n | 結 果 | コメント |
---|---|---|
FirstName & ' ' & Surname | "Fred Smith" | 最初の FirstName の後にスペースを続け、次に Surname を連結する |
Address.(Street & ', ' & City) | "Hursley Park, Winchester" | 括弧 の別の使い方 |
数値式
数値を指すパス式はその値を返します。数値は、通常の数学演算子を使用して結合して、計算結果の数値を生成することができます。サポートされる演算子:
-
+
加算 (足し算、addition) -
-
減算 (引き算、subtraction) -
*
乗算 (掛け算、multiplication) -
/
除算 (割り算、division) -
%
剰余 (割った余り、remainder, modulo)
次の JSON ドキュメントを考えてみよう:
{
"Numbers": [1, 2.4, 3.5, 10, 20.9, 30]
}
例:
E x p r e s s i o n | 結 果 | コメント |
---|---|---|
Numbers[0] + Numbers[1] | 3.4 | 2つの価格を加算 |
Numbers[0] - Numbers[4] | -19.9 | 減算 |
Numbers[0] * Numbers[5] | 30 | 価格と数量を乗算 |
Numbers[0] / Numbers[4] | 0.04784688995215 | 除算 |
Numbers[2] % Numbers[5] | 3.5 | 剰余 |
比較式
2つの値の比較のために、述部でよく使用されます。ブール値 true
または false
を返します。サポートされる演算子:
-
=
等しい (equals) -
!=
等しくない (not equals) -
<
未満 (less than) -
<=
以下 (less than or equal) -
>
より上 (greater than) -
>=
以上 (greater than or equal) -
in
値が配列に含まれている (value is contained in an array)
例:
E x p r e s s i o n | 結 果 | コメント |
---|---|---|
Numbers[0] = Numbers[5] | false | 等しい |
Numbers[0] != Numbers[4] | true | 等しくない |
Numbers[1] < Numbers[5] | true | 未満 |
Numbers[1] <= Numbers[5] | true | 以下 |
Numbers[2] > Numbers[4] | false | より上 |
Numbers[2] >= Numbers[4] | false | 以上 |
"01962 001234" in Phone.number | true | 値が含まれる |
ブール式 (真偽式)
多くの場合、より洗練された述語式をサポートするため、ブール(真偽)結果を結合するために使用されます。サポートされる演算子:
-
and
論理積 -
or
論理和
not
は演算子ではなく関数としてサポートされていることに注意してください。
例:
E x p r e s s i o n | 結 果 | コメント |
---|---|---|
(Numbers[2] != 0) and (Numbers[5] != Numbers[1]) | true | 論理積 |
(Numbers[2] != 0) or (Numbers[5] = Numbers[1]) | true | 論理和 |
出力の作成
元のページはこちら: Language guide / Constructing output
これまで、JSON ドキュメントから値を抽出する方法と、数値、文字列などの演算子を使用してデータを操作する方法を説明しました。この処理されたデータの出力形式を指定できると便利です。
配列コンストラクタ
前述のように、ロケーションパスが入力ドキュメントの複数の値と一致する場合、これらの値は配列として返されます。これらの値はオブジェクトまたは配列なので独自の構造を持ちますが、一致したその値自体は結果の配列の最上位にあります。
ロケーションパス式内で配列(またはオブジェクト)の構成を指定することにより、結果として得られる配列に追加の構造を構築することが可能です。フィールド参照が期待されるロケーションパスの任意の場所で、角括弧 []
を挿入して、それらの角括弧内の式の結果を新しい配列として出力に含めるよう指定することができます。カンマ ,
は、配列コンストラクタ内の複数の式を区切るために使用されます。
ワイルドカードが幅広くマッチしすぎる場合、複数の選択を行うため配列コンストラクタをロケーションパス内で使用することもできます。
例:
E x p r e s s i o n | 結 果 | コメント |
---|---|---|
Email.address | [ "fred.smith@my-work.com", "fsmith@my-work.com", "freddy@my-social.com", "frederic.smith@very-serious.com" ] | 4つの emailアドレス をフラットな配列で返す |
Email.[address] | [ [ "fred.smith@my-work.com", "fsmith@my-work.com" ], [ "freddy@my-social.com", "frederic.smith@very-serious.com" ] ] | 各 email メールオブジェクトはアドレスの配列を生成する |
[Address, Other.`Alternative.Address`].City | [ "Winchester", "London" ] | オブジェクトと Alternative.Address オブジェクトの City 値を選択 |
【訳注】最後の例は **.City
JSONata 式との違い(ワイルドカードの弊害)がわかりにくいので、あまり良い例ではないと感じます。サンプルのコンテキストにもう1つ要素を追加し、以下のように試してみると違いがわかります。
sampleContext["foo"] = {City: "boo"}; // City 項目をもつ(邪魔な!)オブジェクトを追加
var expression = jsonata("[Address, Other.`Alternative.Address`].City");
expression.evaluate(sampleContext); // ["Winchester","London"] を返す
var expression2 = jsonata("**.City");
expression2.evaluate(sampleContext); // ["Winchester","London","boo"] を返す
オブジェクト・コンストラクタ
配列の構築と同様に、JSON オブジェクトも出力で組み立てることができます。
フィールド参照が予想されるロケーションパスの任意のポイントで、中括弧 {}
の中に、キーと値がコロン :
で区切られたセットを、コンマで区切って並べます: {key1:value2, key2:value2}
キーと値はリテラルでも、式でもかまいません。ただしキーは文字列か、文字列に評価される式でなければなりません。
オブジェクト・コンストラクタが複数の値を選択する式に従うとき、オブジェクトコンストラクタは、それらの各コンテキスト値のキー/値のペアを含む単一のオブジェクトを作成します。 オブジェクトの配列(各コンテキスト値に対して1つ)が必要であれば、オブジェクトコンストラクタはドット .
演算子の直後に置かなければなりません。
例:
E x p r e s s i o n | 結 果 | コメント |
---|---|---|
Phone{type: number} | { "home": "0203 544 1234", "office": "01962 001235", "mobile": "077 7700 1234" } | キーの重複により office number の1つが失われました |
Phone.{type: number} | [ { "home": "0203 544 1234" }, { "office": "01962 001234" }, { "office": "01962 001235" }, { "mobile": "077 7700 1234" } ] | オブジェクトの配列を生成 |
JSON リテラル (literals)
配列とオブジェクトのコンストラクタは、JSON 配列と JSON オブジェクトに対して標準の JSON 構文を使用します。以下のネイティブな JSON 構文を使用し、追加の JSON データ型の値を式に入力することもできます:
- 文字列 (strings) -
"hello world"
- 数値 (numbers) -
34.5
- 真偽値 (Booleans) -
true
orfalse
- ヌル (nulls) -
null
- オブジェクト (objects) -
{"key1": "value1", "key2": "value2"}
- 配列 (arrays) -
["value1", "value2"]
これは、有効な JSON 文書が有効な式であることを意味します。このプロパティを使用すると、JSON ドキュメントを目的の出力のテンプレートとして使用し、その一部を式で置き換えて、入力ドキュメントにデータを挿入したものを出力できます。
プログラミング構造
元のページはこちら: Language guide / Programming constructs
これまでは、入力 JSON ドキュメントからデータを抽出し、文字列や数値の演算子を使用してデータを結合し、出力 JSON ドキュメントの構造をフォーマットするために必要な、この言語一部分を紹介してきました。 これから先は、それをチューリング完全な関数型プログラミング言語として使う方法について説明していきます。
条件式
If/then/else の構文は、三項演算子 ? :
を使って書くことができます。
条件式 ? 式1 : 式2
まず 条件式
が評価されます。有効な真偽値(定義参照)が true
の場合、式1
が評価されて返されます。そうでない場合、式2
が評価されて返されます。
括弧で囲まれた式とブロック
演算子の優先ルールを変更(上書き)するために括弧 ()
を使用します。例えば:
(5 + 3) * 4
コンテキスト値で複雑な式を計算するために使用されます。
-
Product.(Price * Quantity)
- Price と Quantity の両方が商品価値の項目です
コードブロック をサポートするために使用されます。複数の式がセミコロンで区切られています。
(式1; 式2; 式3)
ブロック内の各式は、順番に評価されます。最後の式の結果がブロックから返されます。
変数
$
で始まる名前はすべて変数です。変数は値への名前付き参照です。 値は、言語の型システムの任意の型のいずれかになります。
組み込み (Built-in) 変数
-
$
名前のない変数は、入力 JSON 階層の任意のポイントのコンテキスト値を参照します -
$$
入力 JSON のルート。他のパスを一時的に移動するために現在のコンテキストから抜け出す必要がある場合にのみ必要です。例えば、データを相互参照または結合するためのものです。 - ネイティブ (built-in) の関数。関数ライブラリ を参照してください。
変数の割り当て
(タイプシステムの任意のタイプの)値を、変数に割り当てることができます:
$変数名 := "値"
格納された値は、後で式 $変数名
を使用して参照できます。
変数のスコープは、変数が割り当てられた ブロック に限定されます。 例えば:
Invoice.(
$p := Product.Price;
$q := Product.Quantity;
$p * $q
)
式は、Invoice (請求書) に含まれる Price (価格) に製品の Quantity (数量) を掛けたものを返します。
関数
関数はファーストクラスの(この言語における構文の基本となる)型で、他のデータ型と同じように変数に格納できます。組み込み関数のライブラリ が提供され、グローバルスコープの変数に割り当てられます。
例えば $uppercase
は組み込み関数の一つで、文字列引数 str
を指定して呼び出すと、str
のすべての文字が大文字に変更された文字列が返されます。
関数の呼び出し
関数は、その参照(または定義)に続いて、コンマで区切られた引数のシーケンスを含む括弧 ()
で呼び出されます。例:
-
$uppercase("Hello")
は文字列 “HELLO” を返す -
$substring("hello world", 0, 5)
は文字列 “hello” を返す -
$sum([1,2,3])
は数値 6 を返す
関数の定義
匿名(ラムダ, lambda)関数は、次の構文を使用して定義できます:
function($l, $w, $h){ $l * $w * $h }
そして以下を使用して呼び出すことができ
function($l, $w, $h){ $l * $w * $h }(10, 10, 5)
これは数値 500 を返します。この関数は、将来の使用のために(ブロック内で)変数に代入することもできます。
(
$volume := function($l, $w, $h){ $l * $w * $h };
$volume(10, 10, 5);
)
再帰関数
変数に割り当てられた関数は、その変数参照を使用して呼び出すことができます。これにより、再帰関数を定義することができます。例えば:
(
$factorial:= function($x){ $x <= 1 ? 1 : $x * $factorial($x-1) };
$factorial(4)
)
純粋に匿名の関数を使用して再帰関数を書くことが実際可能であることに注意してください(つまり、変数には何も割り当てられません)。これは、不動点コンビネータ(Y-combinator) を使用して行われますが、これは関数型プログラミングに関心を持つ人にとっては興味深い転用であるかもしれません。
高次関数
ファーストクラスのデータ型である関数は、別の関数にパラメータとして渡すことも、関数から戻すこともできます。他の関数を処理する関数は高次関数と呼ばれます。次の例を考えてみましょう:
(
$twice := function($f) { function($x){ $f($f($x)) } };
$add3 := function($y){ $y + 3 };
$add6 := $twice($add3);
$add6(7)
)
- 変数
$twice
に格納されている関数は高次関数です。それは関数であるパラメータ$f
をとり「パラメータ$x
に関数$f
を2回適用してそれを返す関数」を返します -
$add3
は引数に3を加える関数を格納します。$twice
や$add3
はまだ呼び出されていません -
$twice
は、引数として関数$add3
を渡すことによって呼び出されます。これは「引数に$add3
を2回適用する関数」を返します。 この返された関数はまだ呼び出されておらず、$add6
に割り当てられます - 最後に
$add6
の関数が引き数 7 で呼び出され、3 が追加される処理($add3
)が2回実行された結果、13 を返します
関数クロージャ (関数閉包)
匿名関数が定義されると、評価エンジンは環境のスナップショットを取得し、それを関数本体の定義と共にに格納します。環境は、現在のスコープ内の変数バインディングとともに、コンテキスト項目(すなわち、ロケーションパス内の現在の値)を含んでいます。
後に匿名関数が呼び出されると、呼び出された匿名関数は呼び出し時(現在)の環境ではなく、格納された環境で実行されます。このプロパティはレキシカル(文脈上の)スコープと呼ばれ、クロージャ(閉包)の基本的なプロパティです。
次の例を考えてみましょう:
Account.(
$AccName := function() { $.'Account Name' };
Order[OrderID = 'order104'].Product.{
'Account': $AccName(),
'SKU-' & $string(ProductID): $.'Product Name'
}
)
関数が作成されると、コンテキスト項目($
によって参照される)は Account
の値になります。その後、関数が呼び出されると、コンテキスト項目は構造体を各 Product
項目の値に移動しました。ただし、関数本体は、定義されたときに格納された環境で呼び出されるため、そのコンテキスト項目は Account
の値になります。
これはやや工夫された例ですが、実際にこんな関数を必要とすることはないでしょう。式は次の結果を生成します:
{
"Account": "Firefly",
"SKU-858383": "Bowler Hat",
"SKU-345664": "Cloak"
}
高度な関数
このセクションを読む必要はありません。JSON データの操作性や能力のためには役に立たないでしょう。
以前は、数値の階乗を計算するための再帰関数を作成する方法を学び、関数の名前を付けずにこれを実行できることを暗示していました。 極限まで高次関数をとり、次のように書くことができます:
λ($f) { λ($x) { $x($x) }( λ($g) { $f( (λ($a) {$g($g)($a)}))})}(λ($f) { λ($n) { $n < 2 ? 1 : $n * $f($n - 1) } })(6)
結果は 720
となる。文字列 function
の代わりにギリシア語のラムダ λ
記号を使用することができます。もしあなたがキーボード上でそれを見つけることができれば、スクリーンスペースを節約し、ラムダ計算のファンを喜ばせるでしょう。
この上の式の最初の部分は、この言語での 不動点コンビネータ(Y-combinator) の実装です。 変数に代入して他の再帰的な無名関数に適用することができます:
(
$Y := λ($f) { λ($x) { $x($x) }( λ($g) { $f( (λ($a) {$g($g)($a)}))})};
[1,2,3,4,5,6,7,8,9] . $Y(λ($f) { λ($n) { $n <= 1 ? $n : $f($n-1) + $f($n-2) } }) ($)
)
フィボナッチ数列を作成する [ 1, 1, 2, 3, 5, 8, 13, 21, 34 ]
しかし、私たちはこれを行う必要はありません。名前付き関数を使う方がはるかに賢明です:
(
$fib := λ($n) { $n <= 1 ? $n : $fib($n-1) + $fib($n-2) };
[1,2,3,4,5,6,7,8,9] . $fib($)
)
おわりに
あまりに長いので、演算子、Operators と 関数ライブラリ、Function Library はさすがに今回は訳しませんでした。反響や要望でもあれば、また土日にちょっと頑張ってしまうかもしれませんが…
今回は 2018年11月18日に JSONata version 1.5.4 を対象とした情報(本家サイト document がちゃんと更新されていれば)を参照しています。
日本語の JSONata 情報はまだ少ないようなので、面白そうなコンテンツがあったら翻訳して紹介していきたいな、とは思っています。
それではまた!