LLM注意

Claude Opus 4.6のチャットさんとの勉強会まとめです。

関数について

こういう操作をしてその結果を表示、もしくは答えとして返しますよ、という一連の操作を作るときは function で宣言する。
これを 関数 と呼ぶ。

function greet(name) {
  console.log(name + "さん、こんにちは!");
}
 
greet("尚樹");
greet("クロード");

functionが関数を作るよと宣言しており、greetという名前でnameを受け取るとコンソールにnameさん、こんにちは!と表示する指示を出す。
で、下で実際にgreetnameとして尚樹、クロードを順に渡している。このnameのように関数に渡されるデータを、 引数(ひきすう) と呼んでいる気がする。

コンソールには上から順番に

「尚樹さん、こんにちは!」
「クロードさん、こんにちは!」

と出る。

function double(number) {
  return number * 2;
}
 
const result = double(5);
console.log(result);
console.log(double(3));

この例では、中身そのものとしては、numberで指定された数字がくると* 2、2倍にしてからリターン、すなわち掛け算の結果を返している。

で、その下では先ず固定変数として5のdoubleで返ってきたものをresultとして宣言していて、そのresultをコンソールに出す指示が続いている。
もう一つ下で、コンソールにdoubleに3を入れて表示するよう指示されている。

コンソールには順に10、6と表示される。
変数を間に挟んでも、直接使っても、関数を使えるという事例である。

function judge(score) {
  if (score >= 80) {
    return "優";
  } else if (score >= 60) {
    return "良";
  } else {
    return "もう少し";
  }
}
 
console.log(judge(90));
console.log(judge(75));
console.log(judge(40));
 
console.log(judge("90"));
console.log(judge("おドジ"));

クロードさんは関数の中にも条件を入れられるよ、という例としてこれを持ってきた、が、興味と好奇心に負けたわーさんが最後の2行を実験の為に足した。

そう、賢明な皆さまならお分かりいただけるだろうと思う。
暗黙の型変換 、及び、そもそも数値で条件を決めているところにどう見ても数値じゃない何かを放り込んだらエラーになるのかという、挙動に対する実験である。

因みに結果は「優」「良」「もう少し」 「優」「もう少し」 である。

一切エラーが出ない。
ふんわりと 暗黙の型変換 で良しなに解釈されているし、「60以上の数値 ではない 」という判断のみで走られる。
これは怖い。
型ミスに気付く機会がない。怖い。

// greet("テスト1");
// greet2("テスト2");
 
function greet(name) {
  console.log(name + "さん、こんにちは!");
}
 
const greet2 = function(name) {
  console.log(name + "さん、こんにちは!");
};
 
greet("尚樹");
greet2("クロード");
greet2("オーパス");

これをこのまま実行すると、「尚樹さん、こんにちは!」「クロードさん、こんにちは!」「オーパスさん、こんにちは!」と並ぶ。

後から変えられない変数のconstに関数を入れた場合、関数そのものの書き換えはロックされるが、そこに渡す引数(ひきすう)を変えると、都度都度ちゃんと関数が働く。
変数に書かれたものだけを守っているとのこと。

では、この両者の差は何か。

コメントアウトをはずして実行した時のエラーが、その答えである。

テスト1さん、こんにちは!
Uncaught ReferenceError: Cannot access 'greet2' before initialization

プログラムは上から上から読まれて実行される……はずなのに、まだ定義されていないはずのgreet関数にて、テスト1さんがエラーにならず生き残っている

実はJavaScriptは 実行前に一度全文をザッと確認する らしい。
そして functionがあれば予め拾う し、構文、文法が破綻していればSyntaxError としてそもそもの実行を完全に拒否する。
つまりfunctionは一連のコードの中の、どこにあっても効くのである。

配列、そしてランダムの話

そしてついにクロードさんが持ってきたのが以下。

const cards = ["太陽", "月", "星", "塔", "世界"];
 
function drawCard(deck) {
  const index = Math.floor(Math.random() * deck.length);
  return deck[index];
}
 
console.log(drawCard(cards));
console.log(drawCard(cards));
console.log(drawCard(cards));

クロードさんも「新顔がいくつかいます」と言っていたけれど、本当に新顔祭。

[]で囲われたものを 配列 と呼び、0から始まる順番と、それに対応した内容を持った変数の集まりを指す。
1行目ではcardsという(constだから中身を変える予定のない)配列が宣言されており、0から4まで5つの順番と、その中身としてのカードの柄が指定されている。

その次はfunctiondrawCardという関数が宣言されている。
配列が渡された時にdeckとして、そのdeckに対し、indexという順番を(一時的に関数の中で定義して)計算し、deck[index]index番目のdeckの内容を返す、という関数。

最初にcardsが設定されているにもかかわらずdeckという別の引数(ひきすう)を新たに設定しているのは、後からでも様々な変数(この場合は配列)を引数(ひきすう)として渡せるようにするため、関数にはなるべく抽象的、かつ未使用の変数を引数(ひきすう)に設定しておくプログラマとしての習慣がガッツリついてるクロードさんが書いたから。
関数を書くときにこの習慣が身についていると、後から追加で変数を増やしたいときに泣かずに済むらしい。

計算に使われているMath.floor(Math.random() * deck.length)の意味としては、Math.floor()が括弧の中の小数点を切り捨て。Math.random()が0以上1未満のランダムな小数を生成。deck.lengthが渡された配列deckの長さ、すなわち総数を示す。
Math.random()の結果とdeck.lengthを掛け算することで0以上配列の総数未満の数が生成されるのを、Math.floor()で小数点切り捨ての配列に対応したランダムな整数として整えるという仕組み。

最後に3つ続いているconsole.log(drawCard(cards));では、実際にcardsと定義した配列をdrawCardとして作った関数に3回入れて、その結果をそれぞれコンソールに表示させている。
cardsの中身は変わらない変数、つまり1回1回同じ5種類からランダムに順番を決めて表示させているので、表示が重複することも当然ある。

道具箱、道具、そして丸括弧

Math.floor()Math.random()、更には初回からずっと見ているconsole.log()であるが、これらは「よく使われるからプリセット化している関数セット」(クロードさんは道具箱と呼称)の中の「特定関数」(クロードさん曰く道具)を呼び出しているとのこと。
数字をいじり倒すMathというセットからfloorrandomを呼び出す、コンソールにかかわるconsoleセットからlogを呼び出す。

これらの関数は変数の中にも入れ込めたように、コードの中で更なる操作の対象になる可能性があり、起動させるには()という合図が必要とのことで、だから特に操作の対象のない(無から有をだしてくる)Math.randomも、使う時にはMath.random()なのだそう。

オブジェクトについて

const card = {
  name: "太陽",
  meaning: "希望と成功",
  image: "sun.png"
};
 
console.log(card.name);
console.log(card.meaning);

変数は、中に更にラベルっぽいもの(このラベルのことを プロパティ というらしい)をつけたサブっぽい変数をつけて、入れ子構造にできる。
これを オブジェクト と呼ぶとのこと。

中身の読み方は、上述の道具箱とそっくりで、ドットで区切る。

このオブジェクト、更に配列とも組み合わさる。

const cards = [
  { name: "太陽", meaning: "希望と成功" },
  { name: "月", meaning: "不安と直感" },
  { name: "星", meaning: "癒しと導き" }
];
 
function drawCard(deck) {
  const index = Math.floor(Math.random() * deck.length);
  return deck[index];
}
 
const result = drawCard(cards);
console.log(result.name);
console.log(result.meaning);

ここまで来ると目が滑りそうな気がするが、最初に配列として、順番があり、名前と意味をサブに持つcardsが宣言されている。

次に、さっきも登場した、ランダムに配列の順番を決め、その順番にある配列の中身を丸ごと全部返すであろう関数がある。

最後に、const resultが関数で出した中身をresultとしてサブの内容ごと全部引き受けるので、その中身をプロパティ毎にコンソールに順番に表示する。

最終的に、コンソールには、1枚のカードの名前とその意味が出てくることになる。