3.6 重複データ検出・並べ替え

(1/1)
重複データ検出・並べ替え
AI生成コンテンツ / AI-generated content
名簿、URLの一覧、商品コードの一覧――同じ行が何度も現れる長いテキストは、仕事でも趣味でも頻繁に出てくる。同じ行を1つにまとめ、見やすい順に並べ替えたい。表計算ソフトを立ち上げるほどでもなく、かといって目で追うには行数が多い。そこで本項では、テキストを貼り付けてボタンを押すだけで、重複する行を1つにまとめ、小さい順または大きい順に並べ替えるツール「重複データ検出・並べ替え」(sortUniq.html)を作る。
まず作成の動機を説明する。続いて、「1.1 Codexのインストール」から「3.5 ToDoリスト」までの記事で取り上げていない要素技術を、1項目ずつ順に解説する。重複をどう見つけるか、どういう順序で並べるか、ファイルの中身をどう読み取るか、時間のかかる処理をどう書くかという4つの観点である。最後にsortUniq.htmlプログラム仕様書を示す。これがCodexに渡すプロンプトそのものになる。

目次

作成の動機

私は「ぱふぅ家のホームページ」を更新する都度、更新したページのURLを一覧に書き足している。この一覧は、検索エンジンのGoogleに新しいページが登録されたかどうかを確かめるために使っている。ところが一覧は更新した日時の順に並んでいるため、同じページを何度も手直しすると同じURLが何度も現れる。しかも、あるページの前後にどんなページがあるのかが分からない。URLの順に並べ替え、重複する行を1つにまとめれば、どのページが登録済みで、どのページがまだかを一覧で確認できる。

重複を除いて並べ替えるだけであれば、既存の手段はいくつもある。下表に主なものを比較する。
重複を除いて並べ替える手段の比較
手段重複を除く並べ替え導入の手間外部送信
テキストエディターで手作業目視で探す手作業なしなし
表計算ソフト機能あり機能ありソフトの購入・起動設定による
UNIX系コマンド(sort、uniq)できるできるWindowsでは別途導入なし
オンラインの変換サイトできるできるなしあり(データを送信)
自作ツール(本項)できるできるなし(ブラウザで開くだけ)なし
表計算ソフトは高機能だが、数万行のテキストを貼り付けて重複を除くだけのために起動するのは大げさである。UNIX系のsortコマンドとuniqコマンドは古くからある定番の道具だが、Windowsで使うには別途導入が必要で、コマンドを打ち込む操作は初心者には敷居が高い。オンラインの変換サイトは手軽だが、入力したデータが相手のサーバーへ送られる。社内の顧客コードや取引先の一覧を貼り付けるわけにはいかない。

そこで、HTMLファイル1本をブラウザで開くだけで動き、外部と一切通信しないツールを作る。「3.4 簡易メモ帳」「3.5 ToDoリスト」と同じ考え方である。下表に、本項で新たに取り上げる要素技術をまとめる。以降、この順に解説する。
本項で新たに取り上げる要素技術
観点要素技術本ツールでの役割
重複を見つけるSetオブジェクト同じ行を1つにまとめる
正規表現による置換改行コードの違いを吸収する
順序を決めるIntl.Collator日本語と数字を人間の感覚どおりに並べる
比較関数小さい順・大きい順を1つの式で切り替える
ラジオボタン並べ替え方式を選ばせる
ファイルを読むFile API利用者が選んだファイルの中身を受け取る
TextDecoder文字コードを自動判定して文字化けを防ぐ
待つ処理を書くasync/await読み込みやコピーの完了を待つ
Clipboard API結果をクリップボードへコピーする

Setオブジェクトで重複を取り除く

「重複する行を1つにまとめる」という処理を、素直に書くとどうなるか。1行目を取り出し、2行目以降のすべてと見比べて同じものがないか確かめる。次に2行目を取り出し、3行目以降と見比べる――という具合に、二重の繰り返しになる。行数が10行なら45回の比較で済むが、1万行になると約5000万回である。パソコンでも数秒から数十秒かかり、そのあいだ画面は固まったままになる。

JavaScriptにはSet(セット)という入れ物が用意されている。Setは「同じ値を2つ以上持たない」という性質を最初から備えており、すでに入っている値を追加しようとしても黙って無視する。中では値を短い符号に変換して整理しているため、何行あっても1行あたりの手間はほぼ一定である。sortUniq.htmlでは、次の1行で重複を取り除いている。
const uniqueLines = [...new Set(lines)];
new Set(lines) は、行の配列 lines をSetに入れ直す。この時点で重複は消えている。前後の [ ] と ... はスプレッド構文といい、「中身を1つずつ取り出して並べる」という意味の記号である。Setは配列ではないため、並べ替えなどの配列用の機能が使えない。そこで、いったんSetに入れて重複を消し、すぐに配列へ戻している。

Setが「同じ」と見なすのは完全に一致する文字列だけである。「Tokyo」と「tokyo」、「ABC」(全角)と「ABC」(半角)、行末に空白がある行とない行は、いずれも別のものとして残る。人間の目には同じに見えても機械には違って見えるという点は、次に述べる改行コードの話にもつながる。

改行コードをそろえる

文章の「行が変わる」という区切りは、画面には何も表示されないが、文字として記録されている。これを改行コードという。改行コードには CR(Carriage Return、番号は16進数で0D)と LF(Line Feed、同じく0A)の2種類があり、その組み合わせ方が環境によって異なる。

名前の由来はタイプライターである。1行打ち終えたら、紙を載せた台(キャリッジ)を左端へ戻し(復帰=Carriage Return)、紙を1行分送る(改行=Line Feed)という2つの動作が必要だった。この2動作をそのまま受け継いだのがWindowsの「CR+LF」、改行だけで済ませたのがLinuxやmacOSの「LF」、復帰だけで済ませたのが古いMac OSの「CR」である。

改行コードが混ざったテキストをそのまま行に分けると、Windowsで作った「東京」の行の末尾にはCRが残り、Linuxで作った「東京」の行には残らない。前項で述べたとおり、Setはこの2つを別のものと見なすため、重複除去に失敗する。そこで、行に分ける前に改行コードをLFへ統一する。
const normalizeLines = (text) => text.replace(/\r\n?/g, '\n').split('\n');
replace() の1つ目に書いた /\r\n?/g正規表現(regular expression)である。正規表現とは、探したい文字の並び方の「型」を、記号を使って表す書き方である。たとえば「数字が3個続く」「行の先頭がアルファベット」といった条件を、短い記号列で言い表せる。1文字ずつ調べる処理を自分で書かずに済むため、文字列を扱うほとんどのプログラミング言語に備わっている。

この式を分解すると、次のようになる。
  1. 前後の / / は「ここからここまでが正規表現である」という囲みである。
  2. \r はCRという文字そのものを指す。目に見えない文字を書き表すための記法である。
  3. \n はLFを指す。
  4. ? は「直前のものが0個または1個」という意味である。したがって \r\n? は「CR単独」と「CR+LF」の両方に当てはまる。
  5. 末尾の g は global(全体)の頭文字で、「最初に見つかった1か所だけでなく、すべて置き換える」という指定である。
つまりこの1行は、「CRの後にLFが続いていてもいなくても、まとめてLF1個に置き換える」という指示である。その結果、どの環境で作られたテキストでもLFだけの状態になり、続く split('\n') で正しく行に分けられる。正規表現には、ほかにも数多くの記法がある。詳しくは参考サイトの「PHPで正規表現」を参照されたい。書き方はPHPでもJavaScriptでもほぼ共通である。

文字コード順と自然順の違い

JavaScriptの配列には、並べ替えを行う sort() というメソッドがある。ところが、何も指定せずに sort() を呼ぶと、思ったとおりの順序にならない。既定の動作は、すべての値を文字列と見なし、文字コードの番号順に並べるからである。

たとえば「item2」「item10」「item1」を並べると、「item1」「item10」「item2」の順になる。1文字ずつ番号を比べていくため、4文字目で「1」と「2」を比べた時点で「item10」が「item2」より前だと決まってしまう。人間は「10」を十という数として読むが、機械は「1」と「0」という2文字として読む。これが文字コード順である。

日本語ではさらに具合が悪い。文字コードの番号は、漢字については主に部首と画数の順に割り当てられており、読み方の順にはなっていない。「愛知」「秋田」「青森」を番号順に並べても、五十音順にはならない。

そこで使うのが Intl.Collator(インテル・コレーター)である。Intl は Internationalization(国際化)、Collator は「照合するもの」を意味する。言語ごとの辞書の並べ方を知っている比較器であり、次のように作る。
const collator = new Intl.Collator('ja', { numeric: true, sensitivity: 'variant' });
'ja' は日本語の規則で比べるという指定である。numeric: true は「文字列の中に現れる数字を数として比べる」という指定で、これにより「item2」が「item10」より前になる。人間の感覚に合うこの順序を自然順(natural order)という。sensitivity: 'variant' は「大文字と小文字、濁点の有無まで区別する」という最も厳しい設定で、見た目が少しでも違う行は別のものとして残す。重複を除くツールでは、勝手に同じものと見なされては困るためである。

「3.5 ToDoリスト」では、同じ目的で localeCompare() という文字列のメソッドを使った。仕組みは同じだが、localeCompare() は呼び出すたびに言語規則の準備を行う。並べ替えでは1万行につき十数万回も比較が発生するため、Intl.Collator を最初に1つだけ作って使い回すほうが速い。扱う行数が多いときはこちらを選ぶ。

比較関数で昇順・降順を切り替える

sort() には、並べ替えの基準となる関数を渡すことができる。この関数を比較関数という。比較関数は2つの値を受け取り、次のいずれかの数値を返す約束になっている。
  1. 負の数を返すと、1つ目の値を前に置く。
  2. 正の数を返すと、1つ目の値を後ろに置く。
  3. 0を返すと、どちらが前でもよいと見なす。
sort() は、この符号だけを見て並べ替えを進める。どういう基準で大小を決めるかは、すべて比較関数に任されている。sortUniq.htmlの並べ替えは次の1行である。

uniqueLines.sort((first, second) => collator.compare(first, second) * direction);
collator.compare(first, second) は、前項で作った比較器を使って2つの行を比べ、負・0・正のいずれかを返す。それに direction を掛けている。direction は、ラジオボタンが「小さい順」なら1、「大きい順」なら-1という数値である。-1を掛ければ返る値の符号が反転し、前後の関係がそっくり入れ替わる。昇順用と降順用に2つの関数を書き分ける必要がない。

なお (first, second) => ... という書き方はアロー関数といい、名前を付けずにその場で関数を作る短い記法である。=> の左が受け取る値、右が返す値である。C言語のように関数を上のほうで定義しておいて名前で呼び出す書き方もできるが、1行で済む処理はこの形で書くのが今日のJavaScriptでは一般的である。

ラジオボタンで並べ替え方式を選ぶ

「小さい順」と「大きい順」のように、いくつかの選択肢から必ず1つだけを選ばせたいときはラジオボタンを使う。名前の由来は自動車のラジオの選局ボタンで、1つ押すと押されていた別のボタンが戻る仕組みからきている。HTMLでは次のように書く。
<label><input type="radio" name="order" value="asc" checked> 小さい順</label>
<label><input type="radio" name="order" value="desc"> 大きい順</label>
要点は name属性である。name が同じものどうしが1つの組と見なされ、その中では1つしか選べなくなる。name が違えば別の組になり、それぞれ独立して選べる。value は、選ばれたときにプログラムが受け取る値である。checked を付けた側が最初から選ばれている状態になる。label要素で囲んでおくと、小さな丸だけでなく「小さい順」という文字をクリックしても選べるようになり、指で操作するときに押しやすい。

選ばれている側の値は、次のようにして取り出す。
document.querySelector('input[name="order"]:checked').value
input[name="order"] は「name属性が order であるinput要素」を指す。続く :checked は疑似クラスと呼ばれ、「選ばれている状態のもの」だけに絞り込む。2つのうち選ばれているのは必ず1つなので、これで現在の選択が得られる。どちらが選ばれているかを変数に控えておく必要はなく、必要になった時点で画面から読み取ればよい。

sortUniq.htmlでは、2つのラジオボタンを fieldset要素で囲み、legend要素に「並べ替え方式」という見出しを付けている。見た目に枠が付くだけでなく、画面読み上げソフトに対して「ここからここまでが1組の選択肢である」と伝わる。

File APIでファイルを読み込む

ブラウザは、表示しているWebページが利用者のパソコンの中を勝手に読むことを禁じている。もし読めてしまえば、ページを開いただけで書類や写真が抜き取られる。ブラウザが中身を読めるのは、利用者が自分の意思で選んだファイルだけである。この受け渡しの仕組みが File API である。

入口となるのは <input type="file"> である。ただし、この部品の見た目はブラウザごとにばらばらで、画面の雰囲気に合わせにくい。そこでsortUniq.htmlでは、input要素そのものは画面から隠し、label要素をボタンのように装飾している。label要素の for属性に input要素のidを書いておくと、labelをクリックしたときに結び付いたinputがクリックされたことになる。利用者から見れば「ファイル読み込み」ボタンを押しただけだが、実際にはブラウザ標準のファイル選択画面が開く。

ファイルが選ばれると change というイベントが起きる。そのときの処理が次である。
const [file] = fileInput.files;
if (!file) return;
inputText.value = decodeFile(await file.arrayBuffer()).replace(/^\uFEFF/, '');
fileInput.files は選ばれたファイルの一覧である。複数選択も可能な仕組みのため、1つだけのときも一覧の形で渡される。const [file] = ... は分割代入といい、一覧の先頭を取り出して file という名前を付ける書き方である。利用者が選択を取り消すと空になるので、その場合は何もせずに処理を抜ける。

file.arrayBuffer() は、ファイルの中身をバイト列(ArrayBuffer)として受け取る。文字列としてではなくバイト列として受け取るのは、どの文字コードで書かれているかを自分で判定するためである。この判定を行うのが decodeFile() で、次項で説明する。末尾の replace(/^\uFEFF/, '') は、先頭に付いている場合がある目印を取り除く処理で、これも次項で述べる。

なお、読み込みが終わった後に fileInput.value = '' として選択状態を消している。同じファイルをもう一度選んだとき、ブラウザは「変化がない」と判断して change イベントを起こさない。いったん空にしておけば、2度目以降も確実に読み込まれる。細かいが、これを忘れると「同じファイルを選び直しても反応しない」という不可解な症状になる。

文字コードを自動判定する

コンピュータは文字をそのまま記憶しているわけではない。文字の1つ1つに番号を割り当て、その番号を何バイトの数値としてどう並べるかを取り決めておく。この取り決めが文字コードである。同じ「あ」でも、UTF-8では3バイト、Shift_JISでは2バイトで表され、並ぶ数値も異なる。書いた側と読む側の取り決めが食い違うと、画面には意味のない記号の羅列が現れる。これが文字化けである。

日本語のテキストファイルでよく使われる文字コードを下表に示す。
日本語のテキストで使われる主な文字コード
文字コード主な用途日本語1文字BOM
UTF-8今日の標準。Web、Linux、macOS3バイト付く場合がある
UTF-16Windows内部、一部のソフトの出力2バイトほぼ必ず付く
Shift_JIS(CP932)Windowsの従来のテキスト、Excelの出力2バイトなし
EUC-JP従来のUNIX系。古い資料に残る2バイトなし
表にある BOM(Byte Order Mark、バイト順マーク)とは、ファイルの先頭に置かれる数バイトの目印である。もともとは、2バイト以上で1文字を表す文字コードにおいて、上位のバイトと下位のバイトのどちらを先に置くかを示すために作られた。UTF-16では先頭が FF FE なら下位が先(リトルエンディアン)、FE FF なら上位が先(ビッグエンディアン)である。UTF-8はバイトの順番が決まっているためBOMは不要だが、「これはUTF-8である」という宣言として EF BB BF を付ける習慣があり、Windowsのメモ帳などが付ける。

残念ながら、テキストファイルには「私はShift_JISです」と書かれた欄はない。読む側が中身から推測するしかない。sortUniq.htmlは、次の3段階で判定している。
  1. 先頭2バイトを見る。FF FE または FE FF であればUTF-16と確定し、その向きで読む。
  2. UTF-8として厳密に読んでみる。UTF-8には「この並びはありえない」というバイトの組み合わせが数多くあるため、規則に外れていればエラーになる。エラーが出なければUTF-8と判断する。
  3. 残るはShift_JISかEUC-JPである。両方で読んでみて、置換文字の数が少ないほうを採用する。
2段階目の「厳密に読む」を行うのが、TextDecoder の fatal オプションである。
return new TextDecoder('utf-8', { fatal: true }).decode(buffer);
fatal は「致命的」という意味で、true にすると、読めないバイトが1つでもあった時点でエラーを起こして処理を止める。既定の false のままだと、読めないバイトを黙って置換文字に変えて読み進めてしまうため、UTF-8かどうかの判定に使えない。エラーが起きたときは try…catch でそれを受け取り、3段階目へ進む。

3段階目に出てくる置換文字とは、Unicodeで U+FFFD と定められた「この文字は読めなかった」ことを表す記号である。ひし形の中に疑問符が入った形で表示される。Shift_JISのつもりで読んだのに実際はEUC-JPだった場合、読めないバイトが大量に生じてこの記号が増える。そこで両方で読み、置換文字の少ないほうを正解と見なす。
const replacementCount = (text) => (text.match(/\uFFFD/g) | []).length;
match() は正規表現に当てはまる箇所をすべて集めて配列で返すが、1つも見つからないときは null を返す。そのままでは件数を数えられないので、| [] と書いて「nullなら空の配列とみなす」ようにしている。この書き方はJavaScriptでよく使われる。

最後に、読み込んだ文字列の先頭にBOMが残っていれば取り除く。JavaScriptでは、BOMは \uFEFF という番号の1文字として現れる。取り除かないと、1行目だけが目に見えない文字を含む別の行として扱われ、重複除去や並べ替えの結果が狂う。

なお、この判定方法は万能ではない。「あ」1文字だけのファイルのように短いテキストでは、どの文字コードで読んでも規則に反しないことがあり、判定を誤る。実務では、扱うファイルの文字コードを事前にそろえておくのが確実である。文字コードそのものの歴史と詳細は、参考サイトの「文字コードの話」に詳しい。
文字コードを自動判定する3段階の手順
文字コードの自動判定

async/awaitで「待つ」処理を書く

ファイルの読み込みやクリップボードへの書き込みは、命令を出してもその場では終わらない。ディスクから読み出す時間や、ブラウザが利用者に許可を確かめる時間がかかる。そのあいだJavaScriptが立ち止まって待っていると、画面のボタンも動かなくなり、利用者からは「固まった」ように見える。

そこでJavaScriptは、時間のかかる命令に対して結果そのものではなくPromise(プロミス、約束)を返す。「今は答えを渡せないが、終わったら必ず知らせる」という引換券のようなものである。プログラムは引換券を受け取ったらいったん先へ進み、答えが届いた時点で続きの処理が呼び出される。

ただし、この書き方をそのまま並べると「終わったら次を呼ぶ」という入れ子が深くなり、読みにくい。それを普通の手順書のように書けるようにしたのが asyncawait である。関数の頭に async を付けると、その中で await が使えるようになる。await を書いた行は、答えが届くまでそこで待ってから次の行へ進む。
fileInput.addEventListener('change', async () => {
const [file] = fileInput.files;
inputText.value = decodeFile(await file.arrayBuffer());
});
await file.arrayBuffer() の行で読み込みの完了を待ち、その結果をそのまま decodeFile() へ渡している。上から下へ読める形になっており、「待つ」という処理が1語で表現されている。待っているあいだブラウザ全体は止まらないので、画面は反応したままである。

待っている処理で問題が起きたときは、通常の try…catch で受け取れる。ただし、受け取り損ねたものは「3.5 ToDoリスト」で扱った window の error イベントには届かない。非同期の失敗は別扱いで、unhandledrejection(処理されなかった約束の不履行)というイベントとして通知される。sortUniq.htmlでは、両方を登録して画面にエラーを表示している。
window.addEventListener('error', (event) => { ... });
window.addEventListener('unhandledrejection', (event) => { ... });
仕様書に書いた「無限ループに陥ったり、システム・エラーが出たときは、画面にエラー情報を表示して終了すること」という一文を、Codexはこの2つのイベント登録として実装した。何も表示されずに止まるプログラムは、利用者にとって最も困る。

Clipboard APIで結果をコピーする

並べ替えた結果は、他のソフトに貼り付けて使う。そのための「コピー」ボタンを実装するのが Clipboard API である。
await navigator.clipboard.writeText(outputText.value);
クリップボードは、ブラウザの中だけでなくパソコン全体で共有される場所である。もしWebページが自由に読み書きできたら、利用者がコピーしたパスワードを盗み見たり、まったく別の文字列にすり替えたりできてしまう。そのためClipboard APIには2つの制限がある。

  1. 安全な文脈(secure context)でしか動かない。https で始まるページ、localhost、および file:// でパソコン上のファイルを直接開いた場合が該当する。社内サーバーの http:// のページでは使えない。
  2. 利用者の操作をきっかけにしないと動かない。ボタンのクリックなどから呼ぶ必要があり、ページを開いた瞬間に勝手にコピーすることはできない。


1つ目の条件は window.isSecureContext で判定できる。true なら安全な文脈である。sortUniq.htmlでは、この条件を満たさない環境のために、古くから使われてきた別の方法へ切り替えている。
outputText.focus();
outputText.select();
if (!document.execCommand('copy')) throw new Error('ブラウザがコピー操作を許可しませんでした。');
テキストボックスの中身を選択状態にしてから execCommand('copy') を呼ぶ。利用者がマウスで文字を選んで Ctrl+C を押す操作を、プログラムで行っているに等しい。この方法は仕様上「非推奨」とされており、いずれ使えなくなる可能性があるが、現時点では対応しているブラウザが多い。新しい方法を先に試し、駄目なら古い方法に切り替えるという二段構えは、フォールバックと呼ばれる定石である。

どちらも失敗したときは、エラーの内容を画面下部の表示欄に出す。この表示欄には aria-live="polite" を指定してあり、画面読み上げソフトが変化に気づいて読み上げる。「コピーしました」「コピーエラー」といった結果が、目で見ても耳で聞いても分かるようにしてある。
重複を除いて並べ替える処理の流れ
重複除去と並べ替えの流れ

重複データ検出・並べ替えのプログラム仕様書

以上を踏まえて、重複データ検出・並べ替え「sortUniq.html」のプログラム仕様書を示す。これがCodexに渡すプロンプトそのものになる。ここまで解説してきたSet、Intl.Collator、TextDecoder といった具体的な部品の名前は、仕様書には1つも書いていない。書いてあるのは「重複する行を1つにまとめる」「ファイルからテキストを入力する場合は、自動エンコードを行う」といった、実現したいことだけである。どの部品を使うかはCodexが選ぶ。人間が決めるのは、何ができれば合格かという線引きである。
プログラム仕様書(プロンプト)
# 重複データ検出・並べ替え

# 目標 入力したテキストから重複行を1つにまとめ、小さい順または大きい順に並び替える。
# ツール名称 重複データ検出・並べ替え
# プログラム・ファイル名 sortUniq.html
# プロジェクト・フォルダ作成 - プログラム・ファイル名の拡張子を除いた主ファイル名と同じ名前のサブフォルダを作成し、以降の作業はサブフォルダで行う。 - すでにサブフォルダがあれば、そのサブフォルダに移動して以降の作業を進める。
## 出力 - プログラム上部にツール名称、バージョン番号、製作者「(c)pahoo.org Powered by Codex」と記載する。 - 画面を左右2ペインに分ける。 - 左ペイン -- 入力テキストボックス(複数行)がある。 -- 「ファイル読み込み」ボタンがあり、これをクリックすると、複数行テキストボックスにファイル内容が読み込まれる。 -- 「並び替え」ボタンがある。 -- 並べ替え方式を選択するラジオボタン「小さい順」「大きい順」がある。初期値は「小さい順」。 - 右ペイン -- 重複データ検出・並べ替え結果を表示する出力テキストボックス(複数行)がある。 -- 「コピー」ボタンがある。
## 入力 - 入力テキストボックスにテキストを入力する。
## 処理 - 「並び替え」ボタンをクリック(タップ)すると、入力テキストボックスの内容で重複する行を1つにまとめ、並べ替え方式にしたがい行単位の並べ替えを行い、出力テキストボックスるに出力する。 - 「コピー」ボタンをクリックすると、出力テキストボックスるの内容をコピーする。 - ファイルからテキストを入力する場合は、自動エンコードを行う。
## 記録 - なし
## 通信 - 外部と通信しない。
## 例外・エラー処理 - 無限ループに陥ったり、システム・エラーが出たときは、画面にエラー情報を表示して終了すること。
# テスト観点・合格条件 - Codexは、ダミーの入力テキストを5本作成し、上記仕様の通りに動作するか検証する。 - 前提条件、制約条件が守られていること。
# 前提条件 - 仕様変更があった場合は、本書を更新する。 - バージョン番号、著作権者、MIT Licenseであることを画面に表示する。 - 仕様で分からないことがあり、その違いが実装結果へ大きく影響する場合はユーザーに質問する。 - 安全かつ合理的に判断できる軽微な事項は、判断内容を明記して作業を進める。 - HTML、CSS、JavaScriptを1つのHTMLファイルに記述する。 - カード画像は別ファイルとしてプロジェクトフォルダ内に配置してよい。 - クライアントPCのブラウザで動作し、スマートフォンでも利用できること。 - HTTPサーバーやNode.jsなどのサーバー技術は使用せず、ブラウザの機能だけで完結すること。 - Canvas APIまたはHTML要素を利用してゲーム画面を描画すること。 - コーディングは「Airbnb JavaScript Style Guide」に可能な限り準拠すること。 - プログラムファイルのコメントに、名称、バージョン、目的、動作環境、著作権表示および使用条件、インストール方法、お問い合わせを記載すること。 - ゲーム名、画面デザイン、説明文は独自のものとし、ユーザー提供素材を除き、第三者の画像、音声、コードを無断使用しないこと。
# 制約条件 - 原則として、インターネットとのデータ送受信は行わないこと。 - 外部ライブラリを使用する場合は、下記のサイトに限定すること https://cdn.jsdelivr.net/ https://cdnjs.cloudflare.com/ https://ajax.googleapis.com/ https://code.jquery.com/ https://ajax.aspnetcdn.com/ - プログラムがMIT Licenseおよび第三者の権利に違反していないこと。 - ユーザーの許可なくプロジェクトフォルダ外のファイルを変更しないこと。 - ユーザーの許可なく既存ファイルを削除しないこと。 - Gitの履歴を破壊する操作を行わないこと。
# 合格判定 - すべての実装とテストが完了したら、テスト結果を一覧で表示する。 - 合格項目、不合格項目、未確認項目を明確に区別する。 - 不合格項目がある場合は原因と修正方針を表示する。 - 未確認項目がある場合は未確認の理由とユーザーによる確認方法を表示する。 - テスト結果を表示した後、合格としてよいかユーザーに質問する。 - ユーザーが合格と回答するまでは、Gitへの最終コミットと配布用ZIPファイルの作成を行わない。 - ユーザーが不合格と回答した場合は、指摘内容を修正して再テストする。
# 簡易取扱説明書の作成 - HTMLファイルと同じ場所に、簡易取扱説明書「README.txt」を作成する。 - 説明書には「プログラムの名称」「バージョン」「目的」「ゲーム・ルール」「動作環境」「著作権表示および使用条件」「インストール方法」「使い方」「操作方法」「成績の保存」「既知の制限」「変更履歴」「お問い合わせ」を含める。 - インストール方法には、ZIPファイルを展開し、本ツールをブラウザで直接開く手順を記載する。 - マウス、キーボード、画面タッチの操作方法を記載する。
# 著作権表示および使用条件 このプログラムは OpenAI社の Codex によって作成し、作者が動作を確認しました。 このプログラムは外部とのデータ通信を行いません。
本アプリケーションはMIT Licenseです。 商用を含む無償利用が可能です。自由に改造できます。 再配布の際は、下記の著作権表記、URLおよび本使用条件を必ず明記してください。
Copyright by (c)studio pahoo https://www.pahoo.org/
MITライセンスについては、下記のリンク先を参考にしてください。 http://ja.wikipedia.org/wiki/MIT_License http://www.opensource.org/licenses/mit-license.php
なお、本アプリケーションの利用または改造によって生じた得失については一切関知いたしません。また、二次利用先の組織・企業・団体の目的・内容・活動については一切関知いたしません。
# お問い合わせ ぱふぅ家のホームページ https://www.pahoo.org/
- サイト案内 - お問い合わせ
# リソース管理 - 作業開始前に、今回の作業が「新規作成(評価用)」「新規作成(配布用)」「メジャーバージョンアップ」「マイナーバージョンアップ」「不具合修正」のどれに該当するかユーザーに質問する。 - 新規作成(評価用)は0.1.0、新規作成(配布用)は1.0.0とする。メジャー、マイナー、不具合修正は既存バージョンを確認して適切に更新する。 - 既存バージョンを確認できない場合は推測せずユーザーに質問する。 - 決定したバージョン番号をツール、README.txt、本書、配布用ZIPファイル名へ反映する。 - ユーザーがテスト結果を合格と判定した後、プログラム、README.txt、本書、および必要なカード画像をGitへコミットする。 - Gitリポジトリが存在しない場合やコミットできない場合は、勝手に新規リポジトリを作成せず、理由を報告して指示を求める。 - コミットメッセージには、プログラム名、バージョン番号、変更区分が分かる内容を使用する。
# 配布ファイルの作成 - ユーザーがテスト結果を合格と判定した後、ツール、README.txt、本書を1つのZIPファイルに圧縮する。 - ZIPファイル名は「プログラム・ファイル名(拡張子を除く)_バージョン番号.zip」とする。 - ZIPファイル内に一時ファイル、テスト用ファイル、Git管理ファイル、デバッグ専用ファイルを含めない。 - 完了後、作成したZIPファイルの絶対パスをユーザーに伝える。
この仕様書を渡した結果、Codexは1回目の実装でほぼ仕様どおりのツールを作った。ダミーの入力テキスト5本による検証も自動で行い、結果を一覧で報告してきた。修正が必要だったのは、行末の空行の扱いと、スマートフォンでの左右2ペインの折り返しだけである。修正後のバージョンは1.0.1とし、配布用のZIPファイルを作成した。

本項で扱った要素技術は、いずれも「文字列を安全に扱う」という一点でつながっている。重複しているように見えて実は違う行、正しく読めているように見えて実は化けている文字、終わったように見えて実はまだ終わっていない処理――これらを取り違えないための道具立てである。Codexはこれらを適切に選んでくれるが、なぜその部品が必要なのかを人間が理解していれば、テスト観点を書くときにも、不具合を報告するときにも役に立つ。
重複データ検出・並べ替えツールの実行
重複データ検出・並べ替えツールの実行

参考サイト

(この項おわり)
header