Help us understand the problem. What is going on with this article?

料理のレシピをJSONで表現してみる試み

はじめに

ある日、ふと「料理のレシピを機械的に表現したい」という思いに駆られました。料理において、例えば「じゃがいもを切る」という工程は、入力として「じゃがいも」を渡すと、出力として「切られたじゃがいも」が出てくる関数のようなものなわけで、その関数群を一定のルールで表記できれば、レシピ全体を表現できそうです。
考えたことを記録として残してみようと思います。

表記ルール

取り扱いやすさを考え、JSONで表現することにしました。AWSのCloudFormationやStep Functionsなどを参考に組み立てています。

概要

レシピは、大きく分けて以下の3つの要素から構成される、と考え、各要素を列挙することにより全体を表現します。

要素 説明
素材(material) 「玉ねぎ」「砂糖」「牛肉」など、料理の材料となるもの
容器(container) 「鍋」「どんぶり」「ボウル」など、素材を入れるもの
操作(action) 「切る」「煮る」など、素材の状態を変えるもの

全体像は以下のような感じです。

全体像
{
  "title": "名前",
  "description": "説明",
  "materials": { /* 素材を列挙 */ },
  "containers": { /* 容器を列挙 */ },
  "actions": { /* 操作を列挙 */ }
}

素材(material)

素材は、名前の通り料理の材料となるものです。記載方法の例は以下です。

素材(玉ねぎ)
{
  …
  "materials": {
    "onion1": {
      "title": "玉ねぎ",
      "type": "onion",
      "description": "新鮮な玉ねぎ"
      "quantity": [
        {
          "amount": 0.5,
          "unit": "pieces"
        },
        {
          "amount": 100,
          "unit": "g"
        }
      ]
    },
    …
  }
}
要素 説明
title 素材の表示名。省略した場合はtypeにより自動で決まる。typeとしてcustomを指定した場合は必須。
type 素材の種別。onion(玉ねぎ)、sugar(砂糖)などを事前に定義しておく。任意の素材としてcustomを指定することも可。
description 素材の説明。省略可。
quantity 分量。「玉ねぎ半個(100g)」のように、1つの素材に複数の分量がある場合があるので配列にする。
amount 素材の量
unit 素材の量の単位。g(グラム)、pieces(個)、tbsp(大さじ)など

後述しますが、今回は「牛丼」を題材として考えたため、typeやunitは牛丼に必要なものしか定義していないです。レシピを増やしながら定義を拡充していきたいところです。

容器(container)

容器は、複数の素材をグループ化するときに利用します。例えば「煮る」という操作は複数の素材に対して同時に行われます。そのような場合は、容器を使って素材をひとまとめにし、その容器に対して「煮る」操作を適用する、と考えます。容器の記載例は以下です。

容器(鍋)
{
  …
  "containers": {
    "pot1": {
      "title": "お鍋",
      "type": "pot",
      "description": "大き目の鍋。蓋も必要。"
    },
    …
  }
}
要素 説明
title 容器の表示名。省略した場合はtypeにより自動で決まる。typeとしてcustomを指定した場合は必須。
type 容器の種別。pot(鍋)、riceBowl(どんぶり)などを事前に定義しておく。任意の容器としてcustomを指定することも可。
description 容器の説明。省略可。

実際に容器を使って素材をグループ化するには、後述する「add」アクションを使います。

操作(action)

操作は、素材や容器に対して適用され、何等かの形で状態を変化させるものです。例は以下です。

操作(玉ねぎを切る->玉ねぎを鍋に入れる)
{
  …
  "actions": {
    "cutOnion": {
      "type": "cut",
      "source": "onion1",
      "description": "繊維と平行に、幅1センチ程度になるようにスライスする。"
    },
    "addOnionToPot": {
      "type": "add",
      "source": "onion1",
      "target": "pot1",
      "description": "玉ねぎを鍋に投入する。",
      "depend": "cutOnion"
    },
  …
  }
}
要素 説明
title 操作の表示名。省略した場合はtypeにより自動で決まる。typeとしてcustomを指定した場合は必須。
type 操作の種別。cut(切る)、add(加える)、stew(煮る)などを事前に定義しておく。任意の操作としてcustomを指定することも可。
source 操作の入力元となる素材、又は容器。typeによっては配列で複数指定も可。
target 操作の出力先となる素材、又は容器。
description 操作の説明。省略可。
depend 依存関係。その操作を行う前に終わらせておくべき操作を指定する。配列で複数指定も可。
until アクションに終了条件(…分経過するまで、柔らかくなるまで、など)がある場合は指定。
type 終了条件のタイプ。今のところtime(…分経過するまで)のみ
value untilの値

上の例だと、玉ねぎを鍋に入れる前には玉ねぎを切っておく必要があるので、addOnionToPot(玉ねぎを鍋に入れる)のdepend属性としてcutOnion(玉ねぎを切る)を指定しています。実行順序ではなく依存関係で考えるのがポイントで、例えば「人参を切る」と「玉ねぎを切る」はどちらを先に実行してもいいので依存関係はありませんが、「玉ねぎの皮をむく」と「玉ねぎを切る」は、皮むきのほうが先のはずなので依存関係を持たせるべきです。

また、addOnionToPotは、容器を使って素材をグループ化している例です。addアクションを使って容器「pot1(鍋)」に「onion1(玉ねぎ)」を追加しています。素材をグループ化するためには実際に手を動かす(鍋に入れる、ボウルに入れる、など)必要があるので、その都度アクションを割り当てる、という発想になります。

全体像

ここまでを踏まえて作った牛丼のレシピが以下となります。

クリックで展開
牛丼.json
{
  "title": "牛丼",
  "description": "薄く切った牛肉とタマネギなどを醤油などで甘辛く煮込み、丼に盛った飯の上に載せた料理",
  "containers": {
    "pot1": {
      "type": "pot"
    },
    "riceBowl1": {
      "type": "riceBowl",
      "description": "一般的などんぶり"
    }
  },
  "materials": {
    "rice1": {
      "type": "rice",
      "quantity": [
        {
          "amount": 1,
          "unit": "go"
        }
      ]
    },
    "water1": {
      "type": "water",
      "quantity": [
        {
          "amount": 150,
          "unit": "ml"
        }
      ]
    },
    "sugar1": {
      "type": "sugar",
      "quantity": [
        {
          "amount": 1,
          "unit": "tbsp"
        }
      ]
    },
    "soySauce1": {
      "type": "soySauce",
      "description": "一般的なこいくち醤油",
      "quantity": [
        {
          "amount": 3,
          "unit": "tbsp"
        }
      ]
    },
    "mirin1": {
      "type": "mirin",
      "quantity": [
        {
          "amount": 3,
          "unit": "tbsp"
        }
      ]
    },
    "ginger_tube1": {
      "type": "ginger_tube",
      "quantity": [
        {
          "amount": 1,
          "unit": "cm"
        }
      ]
    },
    "beefRib": {
      "title": "牛バラ肉",
      "type": "beef",
      "description": "薄く細切りの牛バラ肉",
      "quantity": [
        {
          "amount": 200,
          "unit": "g"
        }
      ]
    },
    "onion1": {
      "type": "onion",
      "quantity": [
        {
          "amount": 0.5,
          "unit": "pieces"
        },
        {
          "amount": 100,
          "unit": "g"
        }
      ]
    }
  },
  "actions": {
    "cookRice": {
      "type": "cookRice",
      "source": "rice1",
      "description": "炊飯器で米を炊く。"
    },
    "makeBroth": {
      "title": "煮汁を作る",
      "type": "add",
      "source": [
        "water1",
        "sugar1",
        "soySauce1",
        "mirin1",
        "ginger_tube1"
      ],
      "target": "pot1",
      "description": "各調味料を計量し、鍋に入れる。"
    },
    "cutBeefRib": {
      "type": "cut",
      "source": "beefRib",
      "description": "長辺5センチ程度になるようにカットする。大きさを揃えておくと、味の染み込み具合や食感を均一にできる。"
    },
    "peelOnion": {
      "type": "peel",
      "source": "onion1",
      "description": "2分割して固いところを切り落とした後、素手で皮をむく。"
    },
    "cutOnion": {
      "type": "cut",
      "source": "onion1",
      "description": "繊維と平行に、幅1センチ程度になるようにスライスする。",
      "depend": "peelOnion"
    },
    "boil": {
      "type": "bringToABoil",
      "source": "pot1",
      "description": "調味料が入った鍋を強火で沸騰させる。",
      "depend": "makeBroth"
    },
    "addBeefRibToPot": {
      "type": "add",
      "source": "beefRib",
      "target": "pot1",
      "description": "中火に変更し、牛肉を加えて解きほぐす。",
      "depend": [
        "cutBeefRib",
        "boil"
      ]
    },
    "stew1": {
      "type": "stew",
      "source": "pot1",
      "until": {
        "type": "time",
        "value": 5
      },
      "description": "中火のまま5分間煮込む。灰汁が気になる場合は取り除いておく。",
      "depend": "addBeefRibToPot"
    },
    "addOnionToPot": {
      "type": "add",
      "source": "onion1",
      "target": "pot1",
      "description": "玉ねぎを鍋に投入する。",
      "depend": [
        "cutOnion",
        "stew1"
      ]
    },
    "serve": {
      "type": "serve",
      "source": [
        "rice1",
        "pot1"
      ],
      "target": "riceBowl1",
      "description": "どんぶりにご飯と具材を盛り付ける。",
      "depend": [
        "cookRice"
      ]
    }
  }
}

視覚化

JSONでの表現ができたところで、せっかくなのでビューアを作って視覚化してみました。
recipe.jpg
素材や容器で指定されたtypeに応じて、わかりやすいように画像を表示させています。依存関係をきちんと定義すれば、ワークフローのようにレシピを表現できるはずです。

さいごに

まだ牛丼のことしか考えていないので、表現力が足りていないところがあるかもしれません。他にもレシピをJSON化しながら足りないところを足していきたいです。
また、レシピをこのように構造的に表現できれば、クリティカルパスの計算や、それに基づいた作業順序の推奨、進捗の管理などなど、できることの想像が広がります。色々試してみようと思います。

参考文献

レシピ部分で以下を参考にさせて頂きました。
・チューブ生姜適量ではなくて1cmがいい人の理系の料理

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした