目次
- 言語を深く効率的に学ぶには
- プログラミング言語を俯瞰する
- 文法の誕生
- 処理の流れをコントロールする
- 関数
- エラー処理
- 名前とエスケープ
- 型
- コンテナと文字列
- 並行処理
- オブジェクトとクラス
- 継承によるコードの再利用
言語を深く効率的に学ぶには
- 比較
- 真偽値の扱い
- 歴史
- 当時のComputerの性能、サイズ、用途
プログラミング言語を俯瞰する
- プログラミング言語誕生の歴史
- ケーブルを繋ぐ
- 紙に穴を開ける
- 機械に直接機械語で命令する感じから、より人間が理解しやすい形へ
- 適材適所
- 何を楽にしたいのか が 言語によって異なる
文法の誕生
-
スタックマシン
- ソースコードを頭から順に追って行き、出会った要素を一つずつスタックに積んでいく
- ex FORTH)
12+
なら スタックに1,2が積まれて+の命令が積まれる - PythonではコードがFORTHのようなコードに内部的にコンパイル(変換)される
- ex FORTH)
- ソースコードを頭から順に追って行き、出会った要素を一つずつスタックに積んでいく
-
構文木とLISP
- 構文木
- 命令を一番上に描いて、続く要素を下に並べる -> 連環が木みたいにみえる
- ex) 足せ、1と2を
- LISPはFORTHとは構文が異なるが、同じ処理を構文木に表現すれば同じツリーになる
- 中置記法、後置記法、前置記法
- 計算処理の命令部分をどこに書くか。
1+2
は中置。
- 計算処理の命令部分をどこに書くか。
- ex) 足せ、1と2を
- 命令を一番上に描いて、続く要素を下に並べる -> 連環が木みたいにみえる
- 構文木
処理の流れをコントロールする
- 処理の流れのコントロール
- ifが生まれる前
- gotoで"ジャンプ"させていた
- プログラミングの歴史で概観した通り、機械語を人間の理解しやすい形に直す大きな流れに沿っている。
- gotoでも別に実現できるけど, ifがあった方が理解しやすい
- XXでも可能だが構文を制御することにより可読性やバグの産みづらさにつながる -> より人間向きに標準化されていった
- gotoでも別に実現できるけど, ifがあった方が理解しやすい
- ifが生まれる前
関数
- 関数の役割
- 理解,再利用
- スタック=複数の値を保存可能なデータ構造
- A地点へ戻る を実行する関数Aの実行中にB地点へ戻る関数Bを実行しても、最終的にA地点へ戻れる仕組み
- 戻り先管理専用のメモリを用意し、関数Bが格納されているメモリの値を読み込んでから、戻り先メモリの値を1減らすという命令を行っているから
- メモリ
- スタックの単位がメモリで、メモリに部屋番号が順番についてる。(少なくとも文中ではそう扱われている。)
エラー処理
- except when
失敗の種類
- 失敗の種類ごとに処理を分けたい
- try catch throw
- "例外を投げる"
- try except finally
- 出口を一つにしたい
- 例外の伝播
- 対策:検査例外
- ちょっとよくわからないので今度調べる
名前とエスケープ
- スコープ
- 動的スコープ
- グローバルのスコープと、一時的なスコープがあり、まず後者に該当のpropertyの値が保存されてるか確認し、なければグローバルを参照する
- 静的スコープ
- 関数ごとにスコープをつくる
- 動的スコープ
型
- 数字の表現方法の種類を紹介 -> 2進数が便利、と言う話
- しかしバイナリで表現した2が数値なのか文字なのか言語処理系に伝えるために型が必要
- ユーザー定義型とオブジェクト指向
- オブジェクト指向第一の発明:ユーザー定義型
- C言語の構造体などの、「言語が初めから用意した型を組み合わせて、ユーザーが型を定義できること」
- オブジェクト指向第二の発明:ユーザー定義型
- ユーザー定義型に関数も含むことができる機能 ->クラス
- オブジェクト指向第一の発明:ユーザー定義型
- 仕様としての型
- 修飾子によるアクセス制御 private,public
- コンパイラが型があってるかをチェックしてくれるので、それが仕様との齟齬を確認する方法になる
- 公開部分、非公開部分をわける
- コンパイラが型があってるかをチェックしてくれるので、それが仕様との齟齬を確認する方法になる
- 型は仕様であるという考えを押し進め、実態のない型も生まれる (protocol(interface))
- (一部の)型以外の再利用
- 構成要素の型の、一部が変わる型 が実現できたことで再利用性が向上(ジェネリクス)
- 修飾子によるアクセス制御 private,public
- 動的型付け、静的型付け
- 動的型付け ->実行中に型に変わりうる
- メリット:柔軟な実装
- 静的型付け ->コンパイル時に型を確定(宣言)する
- メリット:コンパイラによるコードの検証しやすさ / パフォーマンス / 保守性
- 動的型付け ->実行中に型に変わりうる
- 型推論
- コンパイル時の型確認のメリットを捨てずに、面倒な型宣言を減らしたい(機械に判断させたい)
コンテナと文字列
- コンテナ
- 複数の値を保存できるもの
- 種類
- 配列
- 値を順番に追加していく(値の位置=追加順)
- 連結リスト
- 値と値の位置をセットで追加していく
- 値の挿入、削除時の計算量が配列に比べて少ない.
- 検索(n番目の位置にある要素を探す)時に、計算量が配列よりも多くなる場合が多い
- 配列
- その他のコンテナ
- 辞書(=ハッシュ,連想配列)
- 辞書とは「文字列と値の対応を要素にとる」コンテナのこと。
- 辞書にも様々な実装方法がある
- ハッシュテーブル
- 木
- 結論、計算量に応じた適材適所
- 辞書(=ハッシュ,連想配列)
並行処理
- 並行処理とは
- 複数の処理を細かく区切って実行する
- 処理の切り替え方
- 協調的マルチタスク: 各タスクがきりのいいところ(切り替え可否)を伝える
- プリエンプティブマルチタスク: 時間で区切って強制的に切り替える
- 処理の切り替え方
- 複数の処理を細かく区切って実行する
- 競合状態(スレッドセーフではない状態)とは
- 並行しているタスクが相互に影響を及ぼしてしまい問題が発生すること
- 競合状態の3条件
- 2つの処理が変数を共有している
- 少なくとも1つの処理がその変数を書き換える
- 片方の処理がひと段落着く前に、もう片方の処理が割り込む可能性がある
- 競合状態を防ぐためのアプローチ
- UNIX:異なるプロセスはメモリを共有しない -> 厳しすぎた。結局共有する仕組み(軽量プロセス)導入 これをスレッドと呼ぶようになる(? p176)
- アクターモデル:複数の処理が情報を共有する手段として「メモリの共有」ではなく「通知を送る」方法を提案
- Erlang,Scalaなどで採用
- TwitterやFBなど大量のメッセージングを行うサービスで使われてる
- Erlang,Scalaなどで採用
- メモリを共有するが、書き換えない
- Haskellでは全ての値が変更不可能
- そこまで厳しくない妥協案
- javaのimmutableパターン(getterは露出、setterはprivate)
- 割り込まない
- 協調的なスレッドを使う
- コルーチン、ファイバーグリーンスレッド(+GCD?)
- 割り込まれると困る処理には印をつける
- ロック、ミューテックス、セマフォ
- ロックの問題点: デッドロックの可能性がある
- トランザクショナルメモリによって解決を試みるが、普及してない
- 該当の処理を別のバージョンを作って試してみて、ダメならやりなおす、という仕組み
- Clojureにはある / Microsoftはキラーアプリが見つからないから実験を中止した
- 該当の処理を別のバージョンを作って試してみて、ダメならやりなおす、という仕組み
- トランザクショナルメモリによって解決を試みるが、普及してない
- ロックの問題点: デッドロックの可能性がある
- ロック、ミューテックス、セマフォ
- 協調的なスレッドを使う
オブジェクトとクラス
- オブジェクトとは
- 「現実世界の模型」
- オブジェクト指向とは
- ユーザー定義型としてのクラスを利用したプログラミング(C++設計者)
- ユーザー定義型と継承を利用したプログラミング説
- オブジェクトどうしがメッセージを送り合う仕組み説
- etc..
- クラスが持つ3つの役割
- まとまったものをつくる生成器
- どういう操作が可能かと言う仕様
- コードを再利用する単位
継承によるコードの再利用
- 継承に対する様々な考え方
- 一般化/特殊化
- 親クラスを一般的なクラスとし、子クラスで目的に特化されたものを実装する考え
- 共通部分の抽出
- 差分実装
- 継承は諸刃の剣
- 深い継承ツリーはコードをわかりにくくする
- Liskovの置換原則
- 子は親が成立する条件/仕様を必ず満たすものとして定義する