これまでにカンファレンスなどで発表した資料をここにまとめます。 随時更新です。
最終更新: 2023-10-17
↓↓↓ 発表日降順で並んでいます
続きを読むJavaScript においては typeof や constructor を参照するだけでは関数と class を見分けることができません。
// ただの関数 function myFunction() {} // class class MyClass {} // typeof では見分けられない typeof myFunction; // => 'function' typeof MyClass; // => 'function' // constructor でも見分けられない myFunction.constructor; // => Function MyClass.constructor; // => Function
関数と class を見分けるには toString() すると良いです。
myFunction.toString(); // => 'function myFunction() {}' MyClass.toString(); // => 'class MyClass {}'
class の場合、toString() した結果が 'class '
から始まります。
もし関数と class を判別する関数を書くとしたらこのようになります。
function isFunction(obj) { return typeof obj === 'function' && !obj.toString().match(/^class /); } function isClass(obj) { return typeof obj === 'function' && obj.toString().match(/^class /); }
isFunction の方の条件を obj.toString().match(/^function /)
にしていないのは、アロー関数を toString() した場合に結果が 'function '
から始まらないからです。
なぜ typeof や constructor だけでは関数と class が見分けられないのでしょうか。
それは class は本質的に関数と同じものだからです。
一昔前 [いつ?] まで JavaScript には class 構文が無くて、人々は function 構文を使って class 相当のものを書いていました。
例えばこのように、
class Rectangle { constructor(height, width) { this.height = height; this.width = width; } area() { return this.height * this.width; } } // ↓↓↓ かつてはこう書いていた function Rectangle(height, width) { this.height = height; this.width = width; } Rectangle.prototype.area = function() { return this.height * this.width; };
メソッドを定義するときに .prototype
を参照しています。このあたりの構文が JavaScript がプロトタイプベース言語であることを思い出させてくれますね。 *1
このように私たちが class 構文で定義しているものは関数でもあるので、二つを見分けるのには特殊なテクニックが必要になってしまっています。
この件を ChatGPT に聞いてみると「JavaScript において class は関数と同等なので二つを区別する必要はありません」と言われました。本当でしょうか?
もう一度 function 構文を用いて定義した Rectangle クラスのコードを引用します。
// コンストラクタ function Rectangle(height, width) { this.height = height; this.width = width; } // area メソッドの定義 Rectangle.prototype.area = function() { return this.height * this.width; };
コンストラクタの中では return で値を返すことをしていません。そのため Rectangle(2, 3)
のように new を付けずに関数として Rectangle を呼び出すと返り値は undefined
になってしまいます。
というわけでやはり関数と class の区別は必要ですね。おのれ ChatGPT 。
function 構文を用いた class 定義、久しぶりに書いたのでとても懐かしい気持ちになりました。
私からは以上です。
*1:実際には現代もこのあたりの事情はまったく変わっていなくて、 class 構文は単なる function 構文のシンタックスシュガーです
6歳になる子供がいる。ある日、自転車に乗りながらこんな話をした、
👦「ねぇ、数字って数えていったらどこまで続くの?」
👨『いい質問だね、どう思う?』
👦「うーん、どこまでも続く?」
👨『そう、数字はどこまでも続くから 1, 2, 3, ... と数えていっても終わりはないんだよ』
本当だろうか?確かめてみよう。
自然数の定義はいろいろあり得るが、代表的かつ簡単な定義はこうだ。
次の公理を満たす集合 ℕ の元を自然数と云う
集合 ℕ と定数 o と関数 S について、
1. o ∈ ℕ
2. 任意の n ∈ ℕ について S(n) ∈ ℕ
3. 任意の n ∈ ℕ について S(n) ≠ o
4. 任意の n, m ∈ ℕ について n ≠ m ならば S(n) ≠ S(m)
この公理を ペアノの公理 と云う。
普段、私たちが素朴に "自然数" と呼んでいるものはこの公理を満たす。
定数 o とは 0 のことで。 関数 S とは「n に対して n の次の数を返す関数」のことだ。
集合 ℕ : 自然数の集合、0, 1, 2, ... 定数 o : 0 関数 S : S(n) = n + 1
さて自然数の定義を見てみたが「自然数は無限にある」とは書かれていなかった。
定義にも公理にも無い以上、自然数が無限にあると主張するには証明が必要だ。
というわけで証明してみよう。
証明にはペアノの公理を使う。証明に取りかかるまえにペアノの公理の気持ちを理解しておこう。
公理 1~4 の "気持ち" を文章にするとこうだ、
さらにペアノの公理の集合 ℕ を絵に描いてみよう、
graph LR; O((o)) -->|"S(o)"| N1(("n₁")); N1(("n₁")) -->|"S(n1)"| N2(("n₂")); N2(("n₂")) -->|"S(n₂)"| N3(("n₃")); N3(("n₃")) -->|"S(n₃)"| NEXT[どこまでも続く...];
o から始まって次の数 (関数 S による変換) を辿っていくとどこまでも続く。この「次の数チェーン」を辿った全体が自然数の集合 ℕ になる。
先ほどの絵を見ていると「うーん、自然数が無限にあるのは自明だなぁ」と思う読者もいるんじゃないか。
気持ちは分かる。さきほどの絵がきれいすぎるのが良くない。
今度はもう少し汚い絵を描いて「自然数ってもしかしたら有限かも」という気持ちも味わってみよう。
次に見ていくのはどれも "このようにはならない" という例である。
もしも次の数チェーンが途中で途切れていたらどうだろう。その場合は「自然数は有限個」ということになりそうだ。
graph LR; O((o)) -->|"S(o)"| N1(("n₁")); N1(("n₁")) -->|"S(n₁)"| N2(("n₂")); N2(("n₂")) -->|"S(n₂)"| N3(("n₃")); N3(("n₃")) --->|...| Nz(("nₓ"));
nₓ
には次の数が無い。なのでチェーンはここで終わり。
ということにはならない。公理 2 が「全ての自然数に次の数があり、かつ、次の数もまた自然数である」ことを要請してくれている。
次の数チェーンの先がループになっている場合も問題がありそうだ。
graph LR; O((o)) -->|"S(o)"| N1(("n₁")); N1(("n₁")) -->|"S(n₁)"| N2(("n₂")); N2(("n₂")) -->|"S(n₂)"| N3(("n₃")); N3(("n₃")) --->|...| Nz(("nₓ")); Nz(("nₓ")) -->|"S(nₓ)"| N2(("n₂"));
全ての自然数に次の数があるという公理を満たしつつ有限個になってしまった。
このようなことも起こらないはずだ。公理 4 によって次の数が被らないことが要請されている。
S(n₁)
と S(nₓ)
の矢印がどちらも n₂
に向いているのは公理 4 に反している。
次の数チェーンがぐるっと一周して元の場所 o
に戻ってくる場合はどうだろう。
graph LR; O((o)) -->|"S(o)"| N1(("n₁")); N1(("n₁")) -->|"S(n₁)"| N2(("n₂")); N2(("n₂")) -->|"S(n₂)"| N3(("n₃")); N3(("n₃")) --->|...| Nz(("nₓ")); Nz(("nₓ")) -->|"S(nₓ)"| O((o));
チェーンは途切れていないし、次の数が被ってもいない。でもループができてしまった。
やはりこのようなことも起こらない。公理 3 が「o は次の数にならない」ことを要請している。
S(nₓ)
の矢印が o
に向いているのはおかしなことだ。
ここまでどうにか自然数有限個の危機は回避してきた。が、自然数が無限に存在していることは全然自明ではない気がしてきた。ちゃんと証明して心から安心したいところだ。
というわけでようやく証明にとりかかろう。
背理法で示す。ペアノの公理を満たす有限集合 ℕ が存在し、その要素数が k であると仮定する。
関数 S によって ℕ の各要素が別の要素に移る様を書き並べると。
左辺に着目すると、公理 2 により全ての要素を S によって移せるため k 個の要素全てが現れていることになる。
一方右辺に着目すると、公理 3 により右辺には o
が現れないため高々 k-1 個の要素しか現れない。
S によって k 個の要素が高々 k-1 個の要素に移されるため、 鳩の巣原理 によりある i, j が存在し、nᵢ ≠ nⱼ, S(nᵢ) = S(nⱼ) となる。しかしこれは公理 4 に矛盾する。
よって仮定に誤りがあり、ペアノの公理を満たす集合 ℕ が存在すればそれは無限集合であることが示された。
子供の質問にちゃんと答えるのは難しい。
私からは以上です。
ビーチサンダルが寒いので最近は靴を履くようになりました。
10月の体重の推移を振り返ります。
9月20日から10月31日にかけて 78.8kg→76.8kg (マイナス2.0kg) という推移でした。
6ヵ月間のグラフを見るとどうにか下降トレンドであることは分かる。
10月の消費は11,425kcalでした。目標通り10,000kcalを超えられてよかった。
ウォーキングとランニングを抽出した記録です。10月は合計で121kmでした。
こちらも目標にしていた100kmを超えられてよかった。
これまでの振り返りはこんな感じでした。
エクササイズ時間・消費cal・移動距離ともに減ってます。
有酸素運動を減らして筋トレを増やしたのが要因です。が、単純に飽きつつある気がしますね。
気を引き締めてがんばります。
今月も消費10,000kcal, ウォーキング+ランニングで100kmを目指せたらいいかなと思います。
年内にあと6kg落として体重70kgを切りたい。がんばるぞ💪
私からは以上です。