「ほんとう」を、決める

よみもの+手を動かす時間:およそ30分

値の種類が、増える日

ここまで、あなたの言語の世界に住む値は、数だけでした。1 + 2x = 10 も、出てくるのはぜんぶ数。今日、この言語ははじめて「はい」「いいえ」を覚えます。

これは「新しい記号がひとつ増える」のとは、重みのちがう出来事です。数とは別の、第二の値の種類が生まれる——専門用語では、型(type)が増えると言います。育った姿がこれです。

① ことばの粒(トークン)
3>くらべ2
② 構造の木
  • くらべる >
    • 3
    • 2
③ 評価された値
true

③の欄に、true。数ではない値が、はじめてそこに現れました。1 + 2 > 42 * 5 == 10 にも書きかえて、観察してください。

気づいてほしいのはこれです。比較も、演算である+ が2つの数を受け取って数を返すように、> は2つの数を受け取って真偽を返します。くらべる記号は6つ——== != < > <= >=

評価器には、1行の追加

レッスン3で書いた evaluate を覚えていますか。「左右を畳んでから、最後の一手」——比較も、まったく同じ畳みかたです。新しいのは、返ってくる値の種類だけ。

JavaScript
// 真偽を返す演算を、評価器に教える
function evaluate(tree) {
  if (tree.value !== undefined) {
    return tree.value; // 葉っぱ:数はその数
  }
  const l = evaluate(tree.left);
  const r = evaluate(tree.right);
  if (tree.op === "+") return l + r;
  if (tree.op === ">") return l > r; // ← 今日の追加は、これだけ
}

// 「1 + 2 > 4」の木(+のまとまりが、>の左の子)
const tree = {
  op: ">",
  left: { op: "+", left: { value: 1 }, right: { value: 2 } },
  right: { value: 4 },
};

console.log(evaluate(tree));

Ctrl+Enter でも実行できます

42 に変えると、答えが true に変わります。レッスン3の演習で「評価器にとって、かけ算の追加は1行」と知ったあなたなら、もう驚かないはずです——比較の追加も、評価器では1行でした。

正直に言っておくと、働かされるのは今回も工程②です。木を組む側には「>+ より浅いところに来る」という階層をひとつ足し、さらにこのあと登場する { } の読みかたも教える必要があります。

もしも、を実装する

コース1のレッスン4で、あなたは「もし」を使いました。今日は、それを作る側です。実験室で確かめてください——これは、あの気温のプログラムの、あなたの言語版です。

① ことばの粒(トークン)
きおん名前==30if合図きおん名前>くらべ25{{おすすめ名前==1}}else合図{{おすすめ名前==2}}おすすめ名前
② 構造の木
  • プログラム
    • 代入きおん =
      • 30
    • もしも
      • 条件
        • くらべる >
          • 名前きおん
          • 25
      • そのとき
        • 代入おすすめ =
          • 1
      • ちがえば
        • 代入おすすめ =
          • 2
    • 名前おすすめ
③ 評価された値
301

1行目を 10 に変えると、③の最後が 2 に変わります。なお、if文そのものは③に値を残しません——この言語は「値を見せるのは式と代入だけ」という設計です。

では、種明かしです。この実験室の評価器がif文を処理している部分は、(すこし整えると)こう書かれています。

const c = evaluate(stmt.cond); // ① 条件の式を畳んで、値にする
if (typeof c !== "boolean") {
throw new Error("条件は、true か false で答えられる式にしてください。"); // ② 真偽か、確かめる
}
if (c) runBlock(stmt.then); // ③ 真なら、thenの側だけ
else if (stmt.else) runBlock(stmt.else); // ④ 偽なら、elseの側だけ

ifの実装に、JavaScriptのifを使っている——ずるいと感じたでしょうか。でも思い出してください、+ の実装にもJavaScriptの + を使ってきました。土台の言語の力を借りて、新しい言語を立ち上げるのが、インタプリタという仕組みです。

コース1で「条件がうそのとき、{ } の中は読み飛ばされる」と学びました。その正体が、上の③④です。選ばれなかった側の runBlock を、呼ばない——木としては組まれているのに、評価が訪れない枝。「読み飛ばし」とは、評価器の不作為のことだったのです。

「1」は、真か

上のコードの②に、立ち止まってください。条件が真偽の値でなかったら、エラーにする——つまりこの言語は、「条件は true/false のみ」と決めたのです。実際に見てみましょう。

② 構造の木
  • もしも
    • 条件
      • 1
    • そのとき
      • 2
③ 評価された値

条件は、true か false で答えられる式にしてください。例:x > 3 / x == 0

①②は何も困っていないのに、③が断る。「1は真か?」と聞かれて、この言語は「答えられません」と返す設計です。

ところが。コース1の終わりに出会ったJavaScriptに、同じ質問をしてみてください。

JavaScript
// JavaScriptに「1は真か?」と聞いてみる
if (1) console.log("JSは、1を真とみなしました");

// では、ほかの値は?
if (0) console.log("0は真"); else console.log("0は、偽あつかい");
if ("") console.log("空文字列は真"); else console.log("空文字列も、偽あつかい");
if ("にわ") console.log("中身のある文字列は、真あつかい");

Ctrl+Enter でも実行できます

JavaScriptは 1 を「真っぽい(truthy)」として通します。0や空文字列は「偽っぽい(falsy)」。同じ質問に、2つの言語が逆の答えを返しました。

どちらが正しい、ではありません。寛容な設計には「0かどうかをいちいち x != 0 と書かなくていい」手軽さがあり、厳格な設計には「if x と書きまちがえた瞬間に教えてもらえる」早さがあります。Pythonは寛容の側、Javaは厳格の側——「何を真とするか」という真理の境界線すら、言語設計者が引いた線なのです。

よりみち:真偽の計算は、コンピュータより100年早い

コース1で名前の出たジョージ・ブールは、1854年に「真と偽は、数のように計算できる」と示しました。最初のプログラミング言語より、およそ100年早い発明です。それを電気のスイッチと結びつけたのが、1937年の21歳の大学院生クロード・シャノンの修士論文——今日あなたが書いた「真偽を返す演算」は、この2人の仕事の上に立っています。

くらべた結果は、くらべられない

もうひとつ、この言語が引いた線があります。数学のnoteには「10 ≦ x ≦ 20」と自然に書きますが——

① ことばの粒(トークン)
1<くらべ2<くらべ3
② 構造の木

まだ木になりません — くらべた結果を、さらにくらべることはできません。

③ 評価された値

くらべた結果を、さらにくらべることはできません。例:a < b < c とは書けません。

①は読めるのに、②が断ります。1 < 2 の結果は true であって数ではないので、それをさらに 3 とくらべる意味が定まらない——だからこの言語は、文法の段階できっぱり断る設計にしました。

実は、ここも設計の分岐点です。Pythonは 1 < 2 < 3 を数学どおり「1 < 2 かつ 2 < 3」と読む特別ルールを持っています。一方JavaScriptは黙って通しますが、意味は (1 < 2) < 3、つまり「true と3をくらべる」——だから 3 > 2 > 1 は、見た目に反して false になります。

断る、数学に寄りそう、別の意味で通す。同じ1行に、3つの言語が3つの答えを出しました。あなたの言語が選んだ「断る」は、3つのうちでいちばん不親切で、いちばん事故が少ない線です。

演習:範囲を、入れ子で見はる

x が10以上20以下なら はんいない を1にする」プログラムを完成させてください。数学なら 10 <= x <= 20 の一行ですが、あなたの言語ではくらべた結果をさらにくらべられません。下のコードはいま、10以上しか確かめていません——20以下の見はりを足してください。x を 5・15・25 に変えて、0・1・0 と出れば完成です。

② 構造の木
  • プログラム
    • 代入x =
      • 15
    • 代入はんいない =
      • 0
    • もしも
      • 条件
        • くらべる >=
          • 名前x
          • 10
      • そのとき
        • 代入はんいない =
          • 1
    • 名前はんいない
③ 評価された値
1501
ヒント1(考え方)

関所を2つ並べる、と考えます。「10以上か」の関所を通れた者だけが、「20以下か」の関所に進む。両方を通れた者だけが、1になれる。コース1でやった「もしの中の、もし」を、今度は自分の言語でやるのです。

ヒント2(かたち)

いまある if の { } の中に、もうひとつ if を入れます。外側が x >= 10、内側が x <= 20はんいない = 1 は、内側の { } に引っ越します。

こたえ(の一例)

x = 15 のあとを はんいない = 0if x >= 10 { if x <= 20 { はんいない = 1 } }はんいない とします(実験室では改行を入れて書いてください)。5・15・25で 0・1・0、境界の10と20では 1 になるはずです。

内側にifを入れる代わりに、if x >= 10 のあとに else で0に戻す2段の書きかたを選んだ人もいるでしょう。形がこれと違っても、5・15・25 が 0・1・0 になっているなら、それはあなたの設計の正解です。

このレッスンで分かったこと

  • 言語に第二の値の種類 true/false が増えた。比較は「真偽を返す演算」で、評価器への追加は1行
  • ifの実装は「条件を畳む → 真偽か確かめる → 選ばれた側の runBlock だけ呼ぶ」。「読み飛ばし」の正体は、呼ばれない枝
  • 「1は真か?」への答えは言語ごとに違う。寛容にも厳格にも利点があり、真理の境界線すら設計の選択
  • a < b < c の扱いも分岐点——断る(この言語)、数学どおりに読む(Python)、別の意味で通す(JavaScript)