トラック
/
jq
jq
/
演習
/
ラジコンカー
ラジコンカー

ラジコンカー

学習演習

はじめに

関数

jqでは、必要なロジックをまとめるために、自分だけのカスタム関数を定義できます。_関数_は組み込み関数とまったく同じように動作し、入力を受け取って、0個、1個、あるいはそれ以上の出力を出します。

関数を定義する

jqの_関数_は、次の構文で定義できます。

# no arguments
def funcname: expression;

# or with arguments
def funcname(args): expression;
  • defキーワードで始まり、
  • 関数の本体の前にコロンがあり、
  • 本体は単一の式でできていて、
  • セミコロンで終わり、
  • jqの他の構文と同じように、読みやすさのために自由に空白を使えます。

関数をどこに書くか

_関数_は、使う前に定義しなければなりません。次のコードはエラーになります。

def A: B(10);
def B(n): n + 1;
A
# => error: B/1 is not defined

つまり、_関数_はjqコードの先頭、「メイン」の式より前に置く必要があります。

入れ子の関数

_関数_は入れ子にできます。

def A:
    def B(n): n + 1;
    B(10)
    ;
A
# => 11

ここでは、Bの_関数_はAの本体の中でしか見えません。

スコープ

_関数_は、変数と入れ子の関数のために新しいスコープを導入します。

引数

_関数_の引数は、カンマではなく_セミコロン_で区切ります。 たとえば、数値を受け取り、それに数値を足して、さらに数値を掛ける_関数_は次のようになります。

def add_mul(adder; multiplier): (. + adder) * multiplier;

10 | add_mul(5; 4)    # => 60
Note

セミコロンが必要なのは、カンマにはすでにjqでの役割があるからです。それは、ストリームをつなげる演算子です。

セミコロンの代わりにカンマを使うと、_引数が1つ_のadd_mul関数を2回呼び出そうとします。そんな関数は存在しないので、最初の呼び出しで失敗します。

10 | add_mul(5, 4)
# error: add_mul/1 is not defined
引数は_式_です

関数の_引数_は値ではなく、フィルターです。 この意味で、これらは他の言語でコールバックと呼ばれるものと同じように動作します。

add_mul関数を例にしてみましょう。

10 | add_mul(. + 5; . - 2)    # => 200

ここで何が起きているのでしょうか?

  • adder引数には_式_. + 5が入ります
    • 関数が. + adderを行うと、それは. + . + 5になります
    • . == 10なので、これは25と評価されます
  • 同様に、multiplier引数は_式_. - 2です
    • これは8と評価されます
    • すると、結果は25 * 8 == 200になります
引数を値として扱う

_引数_を変数として「実体化」したいこともあるでしょう。

def my_func(arg):
    arg as $arg
    | other stuff ...
;

これには省略記法があります。

def my_func($arg):
    other stuff ...
;

これは単なる「糖衣構文」にすぎないことに注意してください。$の付いていないargという名前も、_関数_のスコープ内に残っています。

アリティ

_関数_にはアリティがあります。つまり、受け取る_引数_の数です。

_関数_は、同じ名前を違う_アリティ_で使うことができます。組み込みのrange関数がその例で、range/1、range/2、range/3がすべて共存しています。

これは、引数を介して状態を引き継ぐ再帰関数を定義するのに便利です。たとえば、mapは次のように実装することも_できます_。

def my_map($accumulator; func):
    if length == 0
        then $accumulator
        else first as $elem | .[1:] | my_map($accumulator + [$elem | func]; func)
    end
    ;

def my_map(func):
    my_map([]; func)
    ;

[1, 2, 3, 4] | my_map(. * 10)   # => [10, 20, 30, 40]

再帰

jqは末尾呼び出し最適化を行いますが、それはアリティが0の関数だけです。

モジュール

jqのモジュールは、関数だけを含むファイルです。 モジュールは、includeまたはimportコマンドでjqのプログラムに取り込みます。

説明

この演習では、ラジコンカーを動かして遊びます。このラジコンカーは、ようやくお金を貯めて買うことができたものです。

車のバッテリーは、最初は満充電(100%)です。 リモコンで車を動かすたびに、車は20メートル進み、バッテリーを1%消費します。 車のニックネームは、作成されるまで分かりません。

ラジコンカーには、2つの情報を表示する素敵なLEDディスプレイが付いています。

  • 走行した合計距離。表示は"<METERS> meters"のようになります。
  • バッテリーの残量。表示は"Battery at <PERCENTAGE>%"のようになります。

バッテリーが0%になると、車を動かすことはできなくなり、バッテリー表示は"Battery empty"になります。

1. 新品のラジコンカーを作成する

new_remote_control_car/0関数を実装して、新品のラジコンカーのオブジェクトを返すようにしましょう。

new_remote_control_car
# => {
#      "battery_percentage": 100,
#      "distance_driven_in_meters": 0,
#      "nickname": null
#    }

2. ニックネーム付きの新品のラジコンカーを作成する

new_remote_control_car/1関数を実装して、指定したニックネームを持つ新品のラジコンカーのオブジェクトを返すようにしましょう。

new_remote_control_car("Blue")
# => {
#      "battery_percentage": 100,
#      "distance_driven_in_meters": 0,
#      "nickname": "Blue"
#    }

3. 距離を表示する

display_distance/0関数を実装しましょう。この関数は車のオブジェクトを入力として受け取り、LEDディスプレイに表示される距離の文字列を出力します。

new_remote_control_car | display_distance
# => "0 meters"

4. バッテリー残量を表示する

display_battery/0関数を実装しましょう。この関数は車のオブジェクトを入力として受け取り、LEDディスプレイに表示されるバッテリー残量の文字列を出力します。

new_remote_control_car | display_battery
# => "Battery at 100%"

バッテリーが0%になると、バッテリー表示は"Battery empty"になります。

5. 走行するとバッテリーと走行距離が変わる

drive/0関数を実装しましょう。この関数は次の処理を行います。

  • 車のオブジェクトを入力として受け取る
  • 走行距離を20メートル増やす
  • バッテリーを1%消費する
  • 変更後の車のオブジェクトを出力する
new_remote_control_car("Red") | drive
# => {
#      "battery_percentage": 99,
#      "distance_driven_in_meters": 20,
#      "nickname": "Red"
#    }

6. バッテリー切れでの走行に対応する

drive/0関数を更新して、バッテリーが切れているとき(0%のとき)は走行距離を増やさず、バッテリーの残量も減らさないようにしましょう。

{
  battery_percentage: 0,
  distance_driven_in_meters: 2000,
  nickname: "Red"
} | drive
# => {
#      "battery_percentage": 0,
#      "distance_driven_in_meters": 2000,
#      "nickname": "Red"
#    }
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
jq Exercism

ラジコンカーを始める準備はできましたか?

Exercismに登録すれば、12個のコンセプト75個の演習、そして本物の人間によるメンタリングとともに、jqを学んでマスターできます。すべて無料です。