30代SEの自由帳

最初のタイトルは頓挫した

カレーで小難しい話をしてみる(その3)

その2はこちら

息抜き回その31

カレー作りの続き。オリジナル言語を定義しつつプログラミングしてみる。

これまでの状況

最初に作った処理フローはこんな感じ。
ただただ長いから関連個所だけ抜粋。

graph TD
材料を用意する-->
鶏肉を一口大に切る-->
人参を一口大に切る-->
玉ねぎを一口大に切る-->
じゃがいもを一口大に切る-->省略

前回は鶏肉を一旦飛ばして人参を切ろうとしてた

材料を切る

考えやすいように、人参1本のgと一口大にした時のgを以下に決める。

人参1本:150g,一口大1個:10g

人参の構造

人参{
  重さ;
}

前回までの処理

人参.重さ = 150;
一口大の人参の数 = 0;

この処理を繰り返す{
  もし( 人参.重さ <= 10 )なら{
    // もう切れないから切れ端も加えておしまい
    一口大の人参の数 += 1;
    切れ端の重さ = 人参.重さ;
    繰り返しを抜ける;
  }
  // 一口大に切る
  人参.重さ -= 10;
  一口大の人参の数 += 1;
}

切れた人参[一口大の人参の数];
カウンタ = 0;
この処理を繰り返す{
  もし( カウンタ >= 一口大の人参の数 )なら{
    // 全部設定終わったから終了
    繰り返しを抜ける;
  }
  // 一口大の人参の重さを設定
  もし( カウンタ = (一口大の人参の数-1) )なら{ // カウンタは0始まりなので、最後の人参のカウンタ=(一口大の人参の数-1)
    切れた人参[カウンタ].重さ = 切れ端の重さ;
  }
  そうじゃないなら{
    切れた人参[カウンタ].重さ = 10;
  }
  カウンタ++;
}

配列だと数が決まってからじゃないと定義できないから、処理が冗長。。
ってことで、ベクター(Vector)的な概念を導入。

今はベクター=要素を後から追加できる配列。程度の概念で。
配列.追加(要素)で末尾に新しい要素を追加できることにする。
あとは配列.要素数で要素数取れることにする。

ベクターを踏まえて書き直し

人参.重さ = 150;
一口大の人参.重さ = 10;
切れた人参;

この処理を繰り返す{
  もし( 人参.重さ <= 一口大の人参.重さ )なら{
    // もう切れないから切れ端も加えておしまい
    余った人参.重さ = 人参.重さ;
    切れた人参.追加(余った人参)
    繰り返しを抜ける;
  }
  // 一口大に切る
  人参.重さ -= 一口大の人参.重さ;
  切れた人参.追加(一口大の人参)
}

だいぶスッキリ。ついでに処理中に一口大の人参の重さを意識して書いてた10ってのを一口大の人参.重さに置き換え。

もともと書いてた10てのは、そのままだと何の数字か分からない、けど何かの効果(意味)があるマジックナンバー(魔法の数字)と呼ばれるもので、メンテの邪魔2なので基本的に消してく3

やっと人参が切れたので、同様に玉ねぎも切る。 玉ねぎの重さは↓で定義

玉ねぎ1個:200g,一口大1個:5g

玉ねぎの構造

玉ねぎ{
  重さ
}
人参.重さ = 150;
一口大の人参.重さ = 10;
切れた人参;

この処理を繰り返す{
  もし( 人参.重さ <= 一口大の人参.重さ )なら{
    // もう切れないから切れ端も加えておしまい
    余った人参.重さ = 人参.重さ;
    切れた人参.追加(余った人参)
    繰り返しを抜ける;
  }
  // 一口大に切る
  人参.重さ -= 一口大の人参.重さ;
  切れた人参.追加(一口大の人参)
}

玉ねぎ.重さ = 200;
一口大の玉ねぎ.重さ = 5;
切れた玉ねぎ;

この処理を繰り返す{
  もし( 玉ねぎ.重さ <= 一口大の玉ねぎ.重さ )なら{
    // もう切れないから切れ端も加えておしまい
    余った玉ねぎ.重さ = 玉ねぎ.重さ;
    切れた玉ねぎ.追加(余った玉ねぎ)
    繰り返しを抜ける;
  }
  // 一口大に切る
  玉ねぎ.重さ -= 一口大の玉ねぎ.重さ;
  切れた玉ねぎ.追加(一口大の玉ねぎ)
}

人参の処理をコピペして、玉ねぎに変えただけ。
ただ、いちいちコピペするの面倒なので、関数の概念を導入。

対象を切る(対象物, 切り分ける重さ){

  切り分けた対象物.重さ = 切り分ける重さ;
  切れた対象物;

  この処理を繰り返す{
    もし( 対象物.重さ <= 切り分ける重さ )なら{
      // もう切れないから切れ端も加えておしまい
      余った対象物.重さ = 対象物.重さ;
      対象物.追加(余った対象物)
      繰り返しを抜ける;
    }
    // 指定された重さに切る
    対象物.重さ -= 切り分ける重さ;
    切れた対象物.追加(切り分けた対象物)
  }

  return 切れた対象物;
}

関数は処理のまとまり。関数名(引数){処理内容}で定義。
引数=関数の呼び出し元から、関数に渡したい値。複数渡したいときは,で区切る。
returnは関数の実行結果はこれですよ。って呼び出し元に返す値。

いま作った関数を使って書き直し

人参.重さ = 150;
一口大の人参.重さ = 10;
切れた人参 = 対象を切る(人参, 一口大の人参.重さ);

玉ねぎ.重さ = 200;
一口大の玉ねぎ.重さ = 5;
切れた玉ねぎ = 対象を切る(玉ねぎ, 一口大の玉ねぎ.重さ);

すごくスッキリ。こんな感じでよく使いそうな処理は関数化しておくと、コードも見やすいし楽が出来る。

そういえば玉ねぎは2個必要だったので、2つにする

一口大の人参.重さ = 10;
人参.重さ = 150;
切れた人参 = 対象を切る(人参, 一口大の人参.重さ);

一口大の玉ねぎ.重さ = 5;
玉ねぎ1.重さ = 200;
切れた玉ねぎ1 = 対象を切る(玉ねぎ1, 一口大の玉ねぎ.重さ);
玉ねぎ2.重さ = 200;
切れた玉ねぎ2 = 対象を切る(玉ねぎ2, 一口大の玉ねぎ.重さ);

あとは残ってる鶏肉とじゃがいも。

じゃがいも1個:100g,一口大1個:20g
鶏肉一口大1個:20g
じゃがいも{
  重さ
}
鶏肉{
  重さ
}
一口大の鶏肉.重さ = 20;
鶏肉.重さ = 300;
切れた鶏肉 = 対象を切る(鶏肉, 一口大の鶏肉.重さ);

一口大の人参.重さ = 10;
人参.重さ = 150;
切れた人参 = 対象を切る(人参, 一口大の人参.重さ);

一口大の玉ねぎ.重さ = 5;
玉ねぎ1.重さ = 200;
切れた玉ねぎ1 = 対象を切る(玉ねぎ1, 一口大の玉ねぎ.重さ);
玉ねぎ2.重さ = 200;
切れた玉ねぎ2 = 対象を切る(玉ねぎ2, 一口大の玉ねぎ.重さ);

一口大のじゃがいも.重さ = 20;
じゃがいも.重さ = 100;
切れたじゃがいも = 対象を切る(じゃがいも, 一口大のじゃがいも.重さ);

これで全部一口大に切れた。今日はここまで。

今日のまとめ

関数の概念を導入したから、次からは色々楽になるはず。
ただ、実は言語として欠陥ある状態で突き進んでるんだよね。どうしたものか。


  1. さくっと終わらせる息抜きのはずが結構時間かかってる。

  2. 仮に玉ねぎの一口大の重さも10に定義して、後からやっぱり大きすぎるから5に変えよう。ってなった時に、マジックナンバーのままだと、どれが人参の10で、どれが玉ねぎの10か分からなくなる

  3. そうすると実値知りたい時に困る。とか思うかも知れないけど、それは基本的には「need not to know」(by白鳥)