2021年7月26日月曜日

世界の塾と学校

中国では学習塾と宿題が禁止になるらしいです。 日本とは比較にならない競争社会のため、少子化を懸念してのことらしい。

中国や韓国、シンガポールの塾事情が熾烈であることは有名ですね。 日本とは比較にもならない。 小学生低学年から夜10時まで勉強と言われていて、さすがにやり過ぎ感はあり、それが良いこととは思いません。 多少の是正にはなるのではないでしょうか。 ただ正直びっくりですよね。

良い機会なので海外の塾や学校の事情に目を向けてみましょう。 ちなみに世界の塾事情は、以下のページが割と詳しいです。 より詳しくはネットで検索してみると、色々な情報が得られます。

ヨーロッパ: 選別型

ヨーロッパは何度もやり直しができる国と、バッサリ選別される国があります。 ヨーロッパに塾や予備校はほとんどありませんが、幼少期から進路を選別される国では、補習塾のようなものはあるらしい。 このページではドイツを例に挙げていますが、結構同じスタイルの国は多いです。 何度もやり直しができる国も、デンマークのように入職年齢を下げる狙いで一部是正を始めている国があります。

ヨーロッパ: 何度でもやり直せる型

イギリスや北欧などでは、いくらでも先取りできますが、いくらでも遅れることもでき、そして何度でもやり直しができます。 聞こえはいいですが、小1から留年が当たり前にあるので、しないように自立学習が必要になってきます。 結果だけみれば職業が安定するのが30歳以上のことが多く、問題は多いです。 自由と自立に重きを置いているのはわかりますが、塾がないことが本当に良いことかはわからないところがあります。

塾はほとんどないと聞きます。 しかしイギリスの富裕層では大英帝国の歴史などを汲んで家庭教師の比率もそれなりにあるため、塾のような存在がまったくない訳ではありません。 塾のほうが家庭教師よりずっと効率良いから、単純に歴史の差ではないかな。

アメリカ: 何度でもやり直せる型 + GPA

アメリカに塾も予備校もほとんどないのは、大学に入るための一発入試がないからと言われてます。 それ自体は確かに良い事かも知れない。 ただ GPA に毒されているのが必ずしも良いものかはわかりません。 なぜなら突然やる気になっても、GPA で判断されれば、それを認められないことになるからです。 また名門大学に行くためには名門私立学校に入るための競争があり、学費はめちゃくちゃ高くなるし、より激しい競争がある。 なんか日本の私立の感覚に近い気がします。

インド:東アジア以上に苛烈?

インドってどうなんだろうと思って調べてみると、3歳までのプレスクールにおける教育なども重視されており、 また小学生になるより前に算数を習っています。 カースト制度から逃れるために IT 教育も他国とは比較にならないほど進んでいるので、塾がどうこう以前の問題として、東アジア以上に苛烈そうです。 また発言や発表を大切にした教育など、欧米に近い教育が重視されているのも注目すべきポイントかも知れません。

東アジアは全体的に熱心

台湾は IT化で一時期より減ったと言われていますが、大学進学率が非常に高いので勉強は非常に熱心です。 タイは日本からかなり塾が進出してますが、どれくらい激しいかはよくわかりません。 フィリピンは英語大国なので、塾以前の話として英語教育がすごく熱心です。 ベトナムも英語教室が人気とか。 インドネシアやマレーシアも日本から結構進出しているので、盛んそうな印象はありますが、実情はよくわかりません。

その他:不明

中南米や中東だとどうなっているんだろうと思いましたが、情報は見つからず。 オーストラリアは補習塾や進学塾が一応あるみたいです。

まとめ

結局のところ、塾があろうとなかろうと競争はどの国にも存在します。 中国で塾がなくなったとしても競争は残ります。 ニュースでいうところの塾が、筆記対策のことを意味しているなら、GPA 重視のアメリカ的な社会になるのかも知れません。 実際のところはもう少し経ってみないとわからなそうですが、詰め込み型から思考力への抜本的な改革と考えることもできそうです。

日本なんかも関心・意欲・態度みたいな謎項目が追加されているので、GPA 型に分類される気はします。 ただ評価基準が謎で、昔も似たようなものはあったけど、ここまで酷くはなかったと思う。 学問の学び方は小中学校に閉じているものではないので、関心・意欲・態度のような尺度に何か意味があるのかなって思いますね。 GPA 型の欠点は、GPA を取ることだけに集中して、本質的な学びにならなくなってしまうことです。

しかし最近の中国はどこへ向かおうとしているのでしょうね。 香港、国内巨大IT規制強化、総量規制、仮想通貨の規制強化、塾の撤廃、ゲーム敵対視、意図的なクラッシュ。これらは何を意味するのでしょうか。 完璧な国家管理主義をどんどん目指しているように見えます。

そういえば中国には昔、科挙がありました。 いつなくなったのかと言えば、なんと明治の 1904年だったらしいです。 1895年の日清戦争に負けて見直したということになります。 こういった過去もあって、教育のドラスティックな改革に躊躇がなく、また実際に成果を出してきたからとも言えそうです。 下のリンクに書いてあることはとても参考になります。 科挙に通った人材は「暗記能力だけ」「受験生の資金不足」「試験問題が時代遅れ」「科挙に合格した官僚は数字にめっぽう弱い」とのことです。 また「実務能力に乏しく、科学技術的な知識を軽視して、文科的な知識を重視するので、科学技術が振興されない」などは、 形は若干形は違えども今の日本が直面している問題と同質のものに思えます。



ところで、Twitter でこんなものを見つけました。



中国はめちゃくちゃ投資してるので、これもその一貫となるのかどうか。

その他の関連報道:

2021年7月22日木曜日

ABCタイピングを作った

ABCタイピングを作りました。



アルファベットも初めての子が ABC の説明なしにタイピングをできるように作ってみました。 アルファベットを見たことがない子からすると、ABC を打つタイピングって、 形と音とシーケンスでおぼえてポチポチするゲームという認識だと思います。 実際いくつかそういうゲームはあるのですが、使い勝手が良くないので、作ってみました。

あとでもう少し面白くするつもり。

2021年7月19日月曜日

JavaScript の keydown は OS で動作が異なる

JavaScript の keydown は OS で動作が異なることに気付きました。 昔は色々と動作が違っていたのは知っているのですが、最近はもう大丈夫だろうと思い込んでました。

1. イベントの発生状態の違い

ほとんどの OS では、例えば Shift キーなどを押しっぱなしにしているとき、keydown イベントが呼ばれ続けます。 でも Linux だけは、最初に押された時しか呼ばれません。 Linux では Chrome も Firefox も最初に押された時しか反応しないので、 どうやらこれは OS の動作の違いなのだとわかりました。

2. キーボード配列の考慮レベルの違い

keydown で取得できる英語の記号文字は、Firefox では使用しているキーボードの配列に準拠したものが取得できます。 しかし Chrome では Linux / Android で英語キーボードのものになってしまいます。 たとえば日本語キーボードで「"」を押しても「@」が返却されます。 イベントの発生状態はまだしも、これはめちゃくちゃ困るのでバグみを感じます。

タイピングゲームを作ってると地味に影響あります。 最近は気になることはだいぶ減りましたが、まだまだ完璧な互換性はないんだなあ。 そして情報がない。

2021年7月14日水曜日

Denoでよく使う書き方まとめ

そろそろ Deno をメインにしても良いんじゃないかと思って使い始めてみてますが、あまり使い方がまとまってないので、自分用にまとめてみました。 これだけわかれば、普通の言語並に使いやすいんじゃないかな。 よく使うとこだけのまとめなので、困った時は公式のリファレンスを見ましょう。 Node.js でよく使う書き方との対比付きです。



sprintf → 標準ライブラリで可能

import { printf, sprintf } from "https://deno.land/std/fmt/printf.ts";
printf("hello %s", "world");
console.log(sprintf("hello %s", "world"));

printf (改行なし) → 少し面倒

REPL 上だとうまく動かないのが難点…。 このコードを見てもわかるように、Deno だと TextEncoder が至るところで登場してきます。
 await Deno.stdout.write(new TextEncoder().encode("hello: "));

コマンドライン引数 → Node.js より簡潔

Node だと配列のインデックスが 2 から始まるのでわかりにくいけど、Deno だと 0 から始まるため直感的でいい。
if (Deno.args.length < 3) {
  console.log("USAGE: test.js [file]");
  Deno.exit(1);
}
for (let i = 0; i < Deno.args.length; i++){
  console.log(Deno.args[i]);
}

sleep → 外部モジュールで OK

Node.js で真っ先にハマるのは sleep でしょう。 たかが sleep のために npm install はしたくないのが Node.js ですが、書き方は色々あるし事故りやすいです。 しかし Deno は外部モジュールを気楽に使えます。 たとえば以下で良いと思う。楽ちん。
import { sleep } from "https://deno.land/x/sleep/mod.ts";
await sleep(5);

ファイル読み込み (一度に全部) → Node.js より少し楽

const text = Deno.readTextFile("input.txt").then(response => {
  console.log(response);
});

ファイル読み込み (複数を同期的に1行ずつ) → Node.js より超楽

readLines が用意されてたので超簡単になりました。 しかし Node.js と同様に終端で empty string を返してくるのが厄介。 個人的バグの踏みやすさ No.1 は間違いなくこれ。 そんな仕様は Node.js と Deno くらいでいつも疑問なのだけど、undefined チェックするしかないのかな。
import { readLines } from "https://deno.land/std/io/mod.ts";

for (let i=0; i<3; i++) {
  const fileReader = await Deno.open(i + ".txt");
  for await (const line of readLines(fileReader)) {
    if (!line) continue;
    console.log(line);
  }
}
今のところ 1行あたりの文字数が多いと動かない問題を確認している。 そんな時は readLines ではなく readStringDelim を使わないといけない (仕様?)。 readLines も readStringDelim を呼び出しているだけなので、何らかの limit に引っ掛かっているみたいだけど、よくわからない。 サイズが大きくなった時は、いろいろ注意しながら作らないと予期せぬバグが発生しやすい。
import { readStringDelim } from "https://deno.land/std/io/mod.ts";

const fileReader = await Deno.open("long-line.txt");
for await (const line of readStringDelim(fileReader, "\n")) {
  if (!line) continue;
  console.log(line);
}

encoding → あまり想定されてない?

Shift JIS のファイル読み込みなどは、いまのところできないように見えます。 パッと見では想定されてないように見えます。 Windows が面倒そう。 一応ライブラリを使えばいけるみたい (下)。

ファイル書き込み

Deno.writeTextFile("./hello.txt", "Hello World!");

ファイルとディレクトリの判別

関数が変数になったり、細かなところが少し変わった。
const stat = await Deno.stat("/dir/path");
if (stat.isDirectory) {
  console.log("This is a directory");
}

glob → 標準ライブラリで可能に

import { expandGlob } from "https://deno.land/std/fs/expand_glob.ts";

for await (const file of expandGlob("**/*.txt", { globstar: true })) {
  console.log(file);
}

時間計測 → Node.js と一緒

console.time("hoge");
// 処理
console.timeEnd("hoge");

シェル実行 → Node.js のほうが楽

TextDecoder が邪魔で、Node.js のほうが楽な件。 cmd でファイルを渡す時にワイルドカードが効かないのは面倒だなあ。 ワイルドカードなどは bash とかの機能だから、起動が速いなどの利点があるのかな。 ただ常用するには辛いものがあるので、bash -c での実行か、glob を利用した実行を想定しているのかも知れない。
// とりあえず実行したいとき
const p = Deno.run({ cmd: ["cp", "a", "b"] });
// 実行結果が欲しいとき
const p = Deno.run({
  cmd: ["ls', "-altr"],
  // cmd: ["ls", "-altr", "*"],  // これが動かないのはつらい
  // cmd: ["bash", "-c", "ls -altr *"],  // これなら動く
  cwd: "working-directory",
  stdout: "piped",
  stderr: "piped",
  stdin: "null" });
let [status, stdout, stderr] = await Promise.all([
  p.status(), p.output(), p.stderrOutput(),
]);
const textDecoder = new TextDecoder();
stdout = textDecoder.decode(stdout);
stderr = textDecoder.decode(stderr);
p.close();

面倒なので、exec ライブラリが今のところ人気みたいです。
import { exec, OutputMode } from "https://deno.land/x/exec/mod.ts";

const result = await exec('bash -c "ls -altr *"',
  { output:OutputMode.StdOut });
console.log(result);
ただそれでもまだ面倒なケースは多いので、私は deno_zx の利用をおすすめします

野良ビルド → 標準ライブラリで可能に

deno bundle test.js > test.js

野良ライブラリの import (npm の代替)

Deno には npm がないのでライブラリのインストール概念が希薄です。 deno.land/x に登録する方法もありますし、GitHub に直接リンクを貼って import もできます。 deno-sqlite を例に挙げてみると、以下のように特定のリビジョンや特定のバージョンも import できます。 node_modules から解法されるのは地味に嬉しい。
import { DB } from "https://deno.land/x/sqlite/mod.ts";
import { DB } from "https://deno.land/x/sqlite@v2.4.0/mod.ts";
import { DB } from "https://raw.githubusercontent.com/dyedgreen/deno-sqlite/master/mod.ts";
import { DB } from "https://raw.githubusercontent.com/dyedgreen/deno-sqlite/v2.4.0/mod.ts";
import { DB } from "https://raw.githubusercontent.com/dyedgreen/deno-sqlite/f9c35896e5a9ec1f375c6172e4e20d7330dfe14f/mod.ts";
コードが増えた時にはバージョンまで記入すると管理が大変なので、 lock files で管理したほうが良さそうです。 Deno は deps.ts に依存性をまとめて書いておくことが推奨されているので、 以下のコマンドでそれを読み取ってキャッシュを保存しておけば、依存性を保存できます。 deps.ts 自体にバージョンは記載されているので、locks.json に保存されるのは比較用のハッシュ値です。 バージョン管理されていないモジュールを使うときには比較用のハッシュ値は必要そうですが、 信頼がおけるソースからロードする場合は、locks.json もなくても良いとわかります。 GitHub にはハッシュ値があるので、最終手段としてはそれでも良さそう。
 deno cache --lock=lock.json --lock-write src/deps.ts
また npm-check-updates のような機能が欲しくなるとも思いますが、udd という便利なやつが出てきたので、これを使うのが良さそうです。


まとめ

標準ライブラリ deno_std が強いので、Node.js より楽に使えます。 セキュリティの話が絡むファイル操作などを Deno.open などの決まった名前空間で利用できるので、セキュリティ管理しやすいし、何より使いやすい。 実行時にセキュリティ権限を許可しないといけないのが面倒だけど、それ以外は良いことしかない。 Node.js より全体的に高機能になっていて、その高機能になった部分は TypeScript で管理されているから、 ブラウザで使いたいと思った時にもビルドすればいける。ビルドも早い。 他言語ならあって当然のものが await を介して自然に使えるようになった結果、かなり使いやすくなった印象です。 await を付け忘れてバグるのは気を付けるしかないけど、いつもストレスが貯まるファイル操作が楽になったのはとても良い。

Deno が一番良いと思ったのは、top-level await と仕様の簡潔さですね。 Node.js はどこまで行っても仕様が複雑で覚えることが多いのと、ベストプラクティスがわからないことが多い。 deno fmt, deno lint といったコマンドで何の準備もなくコードを綺麗にしてくれるのは素晴らしい。 Node.js にも eslint とかのサードパーティプラグインがあったけど、面倒で何もやってなかった。 私は Node.js や Webpack の無限に増え続ける混沌仕様を覚える気はないし、関わらないようにしていたけど、Deno のように全部用意してくれれば従いたくなる。 流行や仕様をよく見ていないと使いこなせない Node.js と違って、Deno はルールの少ない標準ライブラリだけ見れればいい。とても良い。 deno lint に従ってフロントエンドも lint しているとそれなりに綺麗に書けるので、割とおすすめ。 HTML/CSS/JavaScript の実装にもある程度使えて、Deno のエラーが出ないようにするだけで安定した実装ができる。 Go に近い思想で設計されてて、より使いやすく、好感度は高い。

deno lint は長らく unstable だったけど、Deno 1.11 から stable になって本格導入しやすくなってきました。 執筆時点で、unstable で困るのは fs module くらいですかね。 特に気にならないのでもう完全移行できるんじゃないかと思う。

Deno の一番の難点は、Node との互換性のなさ。 同一コードで管理する方法も欲しいですが、私は諦めたのでなるべく早く移行したほうが楽だと思いました。 とは言え最近では、 Node は Deno に寄せ、Deno は Node に寄せるようになってきたので、それほど差がある訳でもないです。 将来的には仕様の綺麗でマスコットのかわいい Node という立ち位置で安定しそうな気がします。

Deno と Node

Deno と Node.js を考えるとき、嫌でもモジュールについて考える必要が出てきます。



モジュールの扱いの違い

Deno はすべてがモジュールとして扱われるけど、Node.js は 基本的にモジュールとしては扱いません。 Node.js は .mjs 拡張子かつ、package.json で { "type" : "module" } を指定した場合に限りモジュールとして動作します。 つまり動作モードが 2つあるのが Node.js なのだけど、動作モードの違いで色々問題が起きる。

(1) モジュールとして動作させる場合 import で読み込み、top-level await が効く。 モジュールとして動作させない場合 require で読み込み、top-level await が効かない。 Node.js は現時点では require を使うのが基本になっているけど、今後は一体どちらをサポートすればいいのかわかりにくい (後述)。 モードの混在は言語仕様としてよろしくない。

(2) モジュールは当然ながら単体で動作します。 これが意味するところは Global Object は使えないということです。 例えば Node.js の __dirname などは Deno に存在しないし、 Node.js もモジュールとして動かそうとすると、__dirname などが使えなくなる。 __dirname は自分で定義しなければいけない。 そのへんが起因となって、モジュールの定義の仕方も違っている。

(3) 再掲しますが、モジュールとして扱うかどうかは package.json に { "type": "module" } があるかどうかで決定します。 package.json をわざわざ作らないといけないので、ちょっとしたスクリプトを書きたいときに困ります。

(4) Node.js では、混在するモードをどのように扱うのでしょうか。package.json 以外にも拡張子で判断します。 .mjs 拡張子で定義されるモジュール群からは、require などを使う .cjs / .js を以下のように呼び出して使えます。
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const cjs = require("test.js");
しかし .cjs から module を呼び出すのは相当大変みたい。 つまり現状では .mjs で作るべきというのはわかったのですが、 これでは既存の遺産がどうなるのかまったく信用できない言語になってるのは致命的と思う。 正直このようなことは考えたくないし、考えさせてはいけないと思う。

Node.js どうすんのこれ

2つのモードができて統一性がなくなってしまうと、無駄に覚えるコストが出てきて良くない。 ブラウザ側を見ても、Chrome と Firefox は既に top-level await に対応しており、Safari も 15 から対応する。 Node.js の require はブラウザの仕組みとズレてきている。

問題を解決しようとするプロジェクトも結構出てきているみたいです。 Deno プロジェクトを Node.js に変換するものであったり、Node.js から Deno globals を呼び出すものがある。 ただ top-level await に動作モードの違いがあるということは、動作モードの異なるライブラリがどんどん混在していくということでもある。

このへんの話を見ていると、Node より Deno のほうがだいぶ楽だなあと思いました。 みんながスーパー Node.js ユーザーではないので、のちのちどうやってサポートされるのかわからんものは困るのです。 Node.js はサポート期間が極めて短い言語なので、Node.js 14 がドロップされた時点で import 側にシフトするという事なのでしょうが、 正直こんなに不安定な状態で突き進んでいるようでは、少し問題を感じますね。 Deno は依存関係周りをどうするのか若干不透明感があるけど、エコシステムが安定しているので、それだけで信頼できる。

Deno はモジュールの作り方がすこし違う

Node.js の node_modules に過度に慣れてしまうと、とりあえず何でもデータをキャッシュしておけばどうにでもなります。 しかし Deno はキャッシュの概念がないので、実装方法も少し変わってきそうです。 例えば何らかのデータベースを読み込む必要がある場合、どうすべきでしょう。

おそらく (1) 実行時に fetch でロードするか、(2) git clone させて実行させるかのどちらか。 毎回ロードするのは面倒なので、2 を想定しているんじゃないかと思います。 ということでデータとモジュールが明確にわかれるのが Deno と言えます。 ただ少し面倒なので 1 もできるように実装して気が付くのは、 Node.js には fetch が存在しないため無駄にライブラリが必要になるということです。 まったく同じ実装にするのは非常に面倒。

で、ライブラリはどう書くのがいいの?

Node.js も Deno も初心者なのでよくわからないけど、以下のように書くのが良いと思いました。 命名規則は Node.js なら main.js / main.mjs、Deno なら mod.js / mod.ts をルートとするのが通例的に良さそう。

Node.js + module (main.mjs)

import * as fs from "fs";
import * as readline from "readline";
import { fileURLToPath } from "url";
import { dirname } from "path";

class YomiDict {
  static async load(filepath) {
    const dict = {};
    if (filepath) {
      const __filename = fileURLToPath(import.meta.url);
      const __dirname = dirname(__filename);
      filepath = __dirname + "/yomi.csv";
    }
    const fileReader = fs.createReadStream(filepath);
    const rl = readline.createInterface({ input:fileReader });
    for await (const line of rl) {
      const arr = line.split(',');
      const word = arr[0];
      const yomis = arr.slice(1);
      dict[word] = yomis;
    }
    const yomiDict = new YomiDict();
    yomiDict.dict = dict;
    return yomiDict;
  }

  constructor() {
    this.dict = {};
  }

  get(word) {
    return this.dict[word];
  }
}

export { YomiDict };

Node.js (main.js)

const fs = require("fs");
const readline = require("readline");

class YomiDict {
  static async load(filepath) {
    const dict = {};
    if (!filepath) {
      filepath = __dirname + "/yomi.csv";
    }
    const fileReader = fs.createReadStream(filepath);
    const rl = readline.createInterface({ input:fileReader });
    for await (const line of rl) {
      const arr = line.split(',');
      const word = arr[0];
      const yomis = arr.slice(1);
      dict[word] = yomis;
    }
    const yomiDict = new YomiDict();
    yomiDict.dict = dict;
    return yomiDict;
  }

  constructor() {
    this.dict = {};
  }

  get(word) {
    return this.dict[word];
  }
}

module.exports = YomiDict;

Deno (mod.js)

import { readLines } from "https://deno.land/std/io/mod.ts";

class YomiDict {
  static async fetch(url) {
    const dict = await fetch(url)
      .then((response) => response.text())
      .then((text) => {
        const d = {};
        text.split("\n").forEach((line) => {
          if (!line) return;
          const arr = line.split(",");
          const word = arr[0];
          const yomis = arr.slice(1);
          d[word] = yomis;
        });
        return d;
      }).catch((e) => {
        console.log(e);
      });
    const yomiDict = new YomiDict();
    yomiDict.dict = dict;
    return yomiDict;
  }

  static async load(filepath) {
    const dict = {};
    if (!filepath) {
      filepath = "./yomi-dict/yomi.csv";
    }
    const fileReader = await Deno.open(filepath);
    for await (const line of readLines(fileReader)) {
      const arr = line.split(",");
      const word = arr[0];
      const yomis = arr.slice(1);
      dict[word] = yomis;
    }
    const yomiDict = new YomiDict();
    yomiDict.dict = dict;
    return yomiDict;
  }

  constructor() {
    this.dict = {};
  }

  get(word) {
    return this.dict[word];
  }
}

export { YomiDict };
将来使うかも知れないので作り方をメモっておきます。 今まで無理やり同期化してたから気付かなかったけど、 ファイル読み込みするとコンストラクタから追い出されるのは、良いんだか悪いんだか…。

2021年7月5日月曜日

ABC の発音練習アプリ Talk ABC を作った

ABC などのアルファベットの発音練習アプリ Talk ABC を作りました。 数日前に Talk Numers を作ったので 30分くらいでできてしまったけど、実用性は高い。 ブラウザの音声読み上げと音声認識をそのまま使っています。



アルファベット単体ではなく、ABC, XYZ, BBACC など、文字数を増やして暗記ゲームにできればよかったけど、 音声認識が文章や単語を想定しているようなので、無理そうでした。 かなりあっさりしたアプリだけど、発音練習には十二分に使い物になります。

ちなみに Desktop / Android の Chrome だけサポートです。 Safari が API をサポートしたと言ってるのでそのうち動きそうですが、今のところ動きません。

2021年7月2日金曜日

英語アプリ Type Numers、Talk Numbers を作った

音声読み上げされた数字をポチポチ入力するゲーム Type Numers を作りました。 また提示された数字を音声入力するゲーム Talk Numbers も一緒に作りました。 ブラウザの音声読み上げと音声認識をそのまま使っています。 環境にもよりますが今でも音声読み上げは 100くらいの言語に対応していて、今後も勝手に増えていきます。



外国語学習でも割と初期からやる数字の数え上げですが、毎回テストするのは芸がない。 シンプルな瞬間暗記ゲームにしたら面白いかなと思って作ってみました。 この手のアプリはまともに作ると殺風景になってしまうので、ネコを歩かせてみました (てきとー)。

Type Numbers は5桁くらいになると英語の練習不足を感じて、少し反応速度が鈍ってきます。 高速に英語を聞き取る練習としては良いかも知れません。 もっと桁数が増えてくるとキツくなってくるので、案外遊べます。 8桁もあれば十分かと思って設定したのですが、もっと桁数多くても良かったかな?

Talk Numbers のほうは結構難しいというか、たまに認識精度が低いかも知れません。 特に one の入力がやたら難しくて死にました。2桁くらいのほうがむしろ簡単。 それ以外の数字なら大丈夫なんですが、WAN とか同じ発音の単語があるからかな? コンテキストを考慮しないと反応しにくいみたいで、one, two, と言えば反応するけど、one 単体だとやたら難しい。 もう 2-3 年もすれば精度も対応ブラウザもマシになると思うので、こちらは将来に期待ですかね。 one apple などと後ろに単語を付ければ簡単に認識するので、英語だけは救済措置を入れておきました。

ちなみに Talk Numbers は Desktop / Android の Chrome だけサポートです。 Safari が API をサポートしたと言ってるのでそのうち動きそうですが、今のところ動きません。