目次
1.初めに
2.「変数の役割」フレームワーク
3.プログラムに関する知識を深める
4.コードを読む際にも適用可能な文章理解の戦略
5.【プログラマー脳】Chapter
1. 初めに
「プログラマー脳」という本を読んでいます。
その中で印象に残ったことや大事だなと思う部分をまとめています。
自分の理解の必要に応じて自分が慣れているJavaScriptで処理を書いています。
2. 「変数の役割」フレームワーク
「変数」や「整数」といった広すぎるチャンクと「numbers_of_customers」といった特定の変数名のような小すぎるチャンクの間に位置する粒度のチャンクを使うべき
11の役割
- 固定値
- 初期化された後に変化しない変数
- 円周率やデータベースから読み込んだ値など
- ステッパー
- ループ処理を行う際に、ループのたびに値が変更されていく変数(forループの変数 iなど)
- ステッパーの値はループが開始されるタイミングで予測できる
- 二分探索を行う際の「(left + right) / 2)」のように繰り返しごとに探索する配列の順番を変えるといった複雑なステッパーが利用される場合もある。
- フラグ
- 何かが発生したことを示したり、何かの情報が含まれることを示す変数
- isErrorやisArchivedなど、フラグは真偽値が一般的だが整数や文字列の場合もある
- ウォーカー(walker)
- 特定のデータ構造を特定のデータ構造を走査する際に使われるパターンの一つ
- ウォーカーは、ツリーやグラフなどのデータ構造を走査するために広く使用されている
- 例えば、オブジェクトのプロパティを再帰的に走査するために、以下のようなウォーカーの実装を考えることができます。
function walkObject(obj: any, callback: (value: any, key: string) => void) {
Object.keys(obj).forEach((key) => {
const value = obj[key];
callback(value, key);
// プロパティの値がオブジェクトである場合、
// そのオブジェクトのプロパティを処理するためにウォーカー関数を再度呼び出す
if (typeof value === "object" && value !== null) {
walkObject(value, callback);
}
});
}
- 直近の値の保持者(last value holder)
- 一連の値を順に処理していく際に、最も最新の値を保持する変数
// last value holder
let lastValue = 0;
for (const currentValue of [1, 2, 3, 4, 5]) {
lastValue = currentValue;
}
- 最も重要な値の保持者(most important value holder)
- 特定の処理やアルゴリズムにおいて最も重要な値を保持するために使用される変数
- 最小値、最大値、条件を満たす最初の値を保持する変数など
- アルゴリズムによって異なりますが、そのアルゴリズムの動作を理解する上で重要な役割を果たす
- 例えば、ソートアルゴリズムにおいて、比較対象の要素を交換する必要がある場合、最も重要な値は比較対象の要素のうち小さい方の値です。この場合、最も重要な値の保持者は、小さい方の値を保持する変数として使用されます。以下に、選択ソートアルゴリズムの例を示します。
function selectionSort(arr: number[]) {
for (let i = 0; i < arr.length - 1; i++) {
// most important value holder
let minIndex = i;
for (let j = i + 1; j < arr.length; j++) {
// 現在の要素が最小値である場合
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
//
if (minIndex !== i) {
// arr[i]とarr[minIndex]の順番を入れ替える
const temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
return arr;
}
この例では、配列内の要素を選択ソートアルゴリズムでソートしています。内側のループで、現在の要素と最小値を比較し、最小値のインデックスを minIndex変数に代入します。この変数は、最も重要な値の保持者であり、その後の条件分岐で、現在の要素が最小値である場合、交換処理を行うために使用されます。
- 収集者 (accumulator)
- データを集めて、1つの編集に集約させるための変数
- 配列内の要素を加算するだけでなく、配列内の要素をフィルタリング、変換、結合するなど、さまざまな方法で使用される
- 例えば、配列内の数値の合計を計算する場合、反復処理中に配列の各要素を加算していく必要があります。この場合、加算の結果を累積するために、収集者と呼ばれる変数が使用されます。以下に、配列内の数値の合計を計算する例を示します。
// accumulator
let sum = 0;
for (const n of [1, 2, 3, 4, 5]) {
sum += n;
}
console.log(sum); // 15
- コンテナ
- 要素を保持して、追加や削除が可能なデータ構造の変数
let arrayContainer: number[]; // 配列型のコンテナ
arrayContainer = [1, 2, 3]; // 値を代入
console.log(arrayContainer); // [1, 2, 3]
let objectContainer: { [key: string]: number }; // オブジェクト型のコンテナ
objectContainer = { a: 1, b: 2, c: 3 }; // 値を代入
console.log(objectContainer); // { a: 1, b: 2, c: 3 }
- フォロワー
- 前の値や次の値を保持して後から参照する役割を持つ変数
- 例としてはリンクリストを走査する際に前の要素を表すポインタ、二分探索における下位インデックスなど
- オーガナイザー
- 値を並べ替えたり、異なる形式で保存するためだけに使われる変数
- テンポラリ変数でもある
- テンポラリ
- 短期間だけ使われる変数で、tempやtという命名がされる
- 変数の値を入れ替えたり、メソッドや関数内で何度も使われる計算結果を保持する
【個人メモ】
変数の役割を明確にしておく
→ コードを見たときに変数の役割を判別することでコードがチャンクされて読みやすくなる
→ 読みやすくなれば、ワーキングメモリの消費を抑えることができる
→ 変数をアイコンにしておけば、よりコードの理解が進むかも?
3. プログラムに関する知識を深める
コード作成者は
何を目指していたのか、
何を達成しようとしていたのか、
その過程でどのような決断がなされたのか
テキスト構造の理解と計画の理解
プログラマがソースコードを理解する2つのレベル
- テキスト構造の理解
- キーワードが何をすることを意味するのか、変数の役割は何かといったプログラムの一部に関する表面的な理解
- 計画の理解
- プログラムを作るときに何を計画していたのか何を目指していたのかを理解すること
- そのコードを書いたプログラマーの目的は、変数やその役割に隠されているだけでなく、コードがどのように構成されて、どのように関連づけられているかを調べることによってより明確になる
多くのプログラマはコード内の注目すべきポイント(フォーカルポイント)から探し始める
- エラーが発生した行
- 多くのリソースを消費していると判断する行
- 別の理由で注目すべき行
そこから発展していき、より大きな概念を理解していく
表面的な理解から、より深い理解に移行する4つのステップ
- フォーカルポイントを見つける
- そのフォーカルポイントから知識を拡張
- 関連している要素から、そのコードに利用されている概念を理解する
- 複数の要素を横断して利用されている概念を理解する
【重要】フォーカルポイントをバラバラに分断するとつながりが見えずらくなってしまうため、どこから読み始めればいいかわからなくなる。どのようにコードが関連づけられているか知る必要がある。
エンジニアがコードを読むときは全体をスキャンする
- 初心者プログラマーは直線的にコードを読んでいきコールスタックを読む、一方で熟達したプログラマーは初めからコールスタックを辿る
- コールスタックに従った読み方は、経験によって身につくことがわかる
4. コードを読む際にも適用可能な文章理解の戦略
活性化 : 関連する事柄を積極的に考え、過去の知識を活性化する
- 過去の知識を意図的に活性化させる良い方法は、一定時間、例えば10分間コードを勉強して、その意味を理解すること
- 知らない概念に遭遇したら、再びコードに集中する前にその概念を勉強するのが良い
- 新しいコードを読むと同時に新しい概念について学習すると、認知負荷がかかりすぎて、概念とコードの両立を効率的にできなくなる可能性がある
監視:文書の理解度を把握し続ける
コードを読むときは自分が何を読んでいるのか、そしてきちんと理解できているのかを把握し続けることが大事。理解できたことだけでなく、わからなかったところも心に留めておくとなお良い。
重要性の判断:文章のどの部分が最も関連性が高いかを判断する
どの行が重要かを重要であるかを認識することはコードを理解するときに非常に効果がある。
どの行が重要かについて、チーム内で意見が分かれることは珍しくない。
さまざまなバックグラウンドを経験している人や初心者などがいることで、さまざまな意見について学ぶための機会だと考えると良い。
推論:文章にはっきりと書かれていない事実を補完する
コードの変数名がビジネス領域において何を意味するかを理解することには意味がある。
変数名はコードがどのようおな目的や内容なのかを知るための重要な手がかりになりうる。
コードを読むときに変数名に意識して着目することはとても重要です。
可視化:読んだ文章の内容を図解して理解を深める
以前に取り上げた状態遷移表の作成やコードの流れのトレースなど、コードを可視化して理解を深める方法はさまざま存在する。
その中の1つである変数が関与するすべての操作をリストアップするという手法は、より深い理解が必要となる非常に複雑なコードを読むのに非常に便利です。
自問自答:その文章について質問を作り、それに答える
- コードの中で用いられている最も重要な事柄を5つあげてください。
- 「事柄」は識別子名、テーマ、クラス、コメントに書かれている情報などが含まれる。
- その重要な事柄を特定するためにどんな手法を用いたでしょうか?
- メソッド名やドキュメント、変数名を調べたり、そのシステムに関する自分に知識を利用した?
- コードの中で用いられている最も重要なコンピュータサイエンスに関連する事柄を5つ挙げてください
- 「事柄」はアルゴリズム、データ構造、何らかの仮説、技術的なテクニックなどが含まれる
- コードの作成者が下した何らかの判断を特定することができましたか?
- 特定のアルゴリズムやデザインパターンの実装、何らかのライブラリやAPIの利用などが該当します
- それらの判断の特定は、どのような仮説をもとに行ったのでしょうか?
- それらの判断には、どのようなメリットがあったのでしょうか?
- それらの判断には、どのようなデメリットの可能性があったのでしょうか?
- その決断とは別の同じ問題を解決する方法をあげられるでしょうか?
要約:文章の短い要約を作成する
下記の作業は要約を開始するにあたって非常に有効な出発点になる
- 読んだコードの最も重要な行を発見する
- すべての変数とそれに関連する操作をリストアップする
- コードの作者が行った決定を特定する
5. 【プログラマー脳】Chapter
「プログラマー脳」のchapter1「コーディング中の混乱を紐解く」
「プログラマー脳」のchapter2「コードを速読する」
「プログラマー脳」のchapter3「プログラミング言語の文法を素早く習得する方法」
「プログラマー脳」のchapter4「複雑なコードの読み方」
Now →「プログラマー脳」のchapter5「コードの深い理解に到達する」
【TODO】「プログラマー脳」のchapter6「プログラミングに関する問題をよりうまく解決するには」
【TODO】「プログラマー脳」のchapter7「思考に潜むバグ」
【TODO】「プログラマー脳」のchapter8「より良い命名を行う方法」
「プログラマー脳」のchapter9「汚いコードとそれによる認知的負荷を避けるための2つのフレームワーク」
【TODO】「プログラマー脳」のchapter10「複雑な問題をより上手に解決するために」
【TODO】「プログラマー脳」のchapter11「コードを書くという行為」
【TODO】「プログラマー脳」のchapter12「より大きなシステムの設計と改善」
【TODO】「プログラマー脳」のchapter13「新しい開発者のオンボーディング」