Hatena::ブログ(Diary)

shouhの日記

2014-12-23

コーディングを支える技術 ~成り立ちから学ぶプログラミング作法~

どんな本?
プログラミング言語の主要な仕様について、なぜそんなものが存在しているのかという「理由」に重きが置かれて解説されている。

感想
実用的な知識は無いが、言語仕様の存在意義に納得できたり、純粋に知識欲を満たせたりするという意味では面白い。

以下、個人的にへえと思ったことや知らなかったことをまとめる。

個人的にへえと思ったこと(雑多)

  • 言語仕様が簡単だと人間にはムズイ、人間にわかりやすい言語は仕様が複雑
    • LISPはルールのシンプルさを採用(基本的に括弧使うだけ)した例
  • 既存仕様との整合性のために変な仕様が残ることがある
    • C# はガベージコレクタを備えるのにデストラクタがある(C++から引き継いでるため)
  • 糖衣構文はマジで大切
    • ifもwhileも糖衣構文
      • 元々は goto ジャンプだけだったが、自由度高すぎてハチャメチャになるのよ

スコープガード

D言語が例外の代わりに導入した概念。確実に行わせたい処理は近くに書くべきとする考え方。

	…
	lock(mutexobj)
	scope(exit) unlock(mutexobj) // このスコープ終了時に unlock を実行
	…

検査例外

Javaで採用した仕組み。メソッドに、そいつ自身が投げる例外を定義すること。そしたらコンパイラレベルで「この例外投げられる可能性あるのに対処してないよ」とかわかる。
でもぶっちゃけ面倒くさいからあまり普及してない。

動的スコープと静的スコープ

// 動的スコープ(擬似言語)
x = 'global'
sub caller{
	x = 'local in caller' // ここでグローバルな名前空間の皮が作られる
	callee()
}
sub callee{
	print x //local in caller」になる
}
caller()

//静的スコープ(pythonの例)
x = 'global'
def caller():
	x = 'local in caller' # この x は caller 内でしか有効ではない.
	                      # 字句的なスコープと一致するから
	                      # 「レキシカルスコープ」とも呼ぶ.
	callee()
def callee():
	print x # 「global」になる
caller()

静的スコープは現時点で採用されているが、まだ完璧じゃない。たとえばこんなん。

x = 'global'
def func():
	x = 'local in func'
	def nest_func
		# 新しいローカル変数 x を定義したい場合もあれば,
		# 直近の上の x を変えたいかもしれないし,
		# グローバル変数の x を変えたいかもしれない.
		# このような違いはどうやって実現する?
		x = 'local in nest_func'

型って何?

メモリ上に格納した値の「種類を示す」もの。このメモリ列はintとして解釈しろよとか、floatで解釈しろよとか。

動的型付けの仕組み
Pythonでいうと、どの型も PyObject 型で表現している。以下はメモリ上でどうなってるかのイメージ。

int型 → [type=int ][intの内容]
str型 → [type=string ][サイズ][ハッシュ値][状態][0][1][2]…


Mix-in

あるクラスAに混ぜ込める用のクラス。混ぜ込むだけなので多重継承にならず、菱型継承も除ける。

菱型継承(見方はクラスAがメソッドmを持っている→classA[a])

       classA[a]
       A      A
       |      |
classAB[a,b]  classAC[a,c]

A,B,C全部欲しいからといってABとAC継承すると、菱型に。
Aにアクセスする際、AB::aなのかAC::aなのかがわからない。

Mix-in の場合、BとCをモジュールとして切り出す。

mixinB[b]  classA[a]  mixinC[c]

ABもACをこれらを継承or取り込み(Rubyで言えばinclude, pythonでいえば継承)する。
A,B,C全部欲しいならA,B,C全部を継承or取り込みすればいい。
全部取り込んだABCからAにアクセスする際は、継承元のAを見れば済む。

文字コード0

文字コードについての歴史も簡潔にまとまっていたためになったので、自分なりにまとめてみる。

文字コード1: モールス信号

A:* ***
B:*** * * *
C:*** * *** *
S:* * *
O:*** *** ***

点の間は短点一つ分空ける。
文字の間は三つ分、単語間は七つ分。

文字コード2: ASCIIEBCDIC

ASCII: American Standard Code for Information Interchange
情報をやりとりするためのアメリカ標準符号。1文字7ビットを使うため、128種類の文字を表現。

ASCIIが出来た当初、シェアを持ってたIBMが独自に開発したのがEBCDIC

文字コード3: 日本語エンコーディング

iso-2020-jp、shift-jiseuc-jpの三つがある。この順番で新しい順。

以下、各文字コードの解説と、それぞれで「aaaあああaaa」を表現した場合のコード例。

iso-2020-jp(JISコード)

61 61 61 1b 24 42 24 22 24 22 24 22 1b 28 42 61 61 61
a- a- a- <shift-> <あ-> <あ-> <あ-> <shift-> a- a- a- 
  • <shift-> はシフト命令で、アルファベットモードとひらがなモードを切り替える用

shift-jis

61 61 61 82 a0 82 a0 82 a0 61 61 61 
a- a- a- <あ-> <あ-> <あ-> a- a- a- 

shift-jisのアイデアは、1バイト文字に使うバイトを、2バイト文字の先頭文字で使わないようにすること。おかげで「この文字は1バイト文字で使うやつじゃない!次は2バイト文字だな!」とわかる。

shift-jisの弊害は、2バイト文字の2バイト目が、1バイト文字の特殊文字と同じバイトになるケースがあること。たとえば「能」は、sjisでは2バイト目がバックスラッシュと同じだから、

//コメント:ある機能
printf("…");

みたいなコードをsjisで書いて、asciiとして解釈したら、改行がエスケープ(\\n)されてしまい、こうなる。

//…\nprintf("…");
    ~
    エスケープされた

euc-jp

61 61 61 a4 a2 a4 a2 a4 a2 61 61 61 
a- a- a- <あ-> <あ-> <あ-> a- a- a- 

基本的には sjis と基本的に同じだが、2バイト文字の2文字目の先頭文字にも、1バイト文字で使うバイトを使わないようにしてある。

文字コード4: マジックコメント

そもそもソースが何の文字コードで書かれたかがわからないのが諸悪の根源。だったらソースに指定してやろうじゃないかというアイデア。

# -*- coding: euc-jp -*- 

ちなみに python はもっときつい設計にした。ソースに ascii 以外の文字がある&マジックコメントがない 場合に「asciiとして読めない奴があるんだけど!?」とエラーを出すようにした。
→だから日本語コメント書いただけでエラーが出て、初心者にはハードルが高く感じる。

文字コード5: Unicode

日本国内だけでもこんなに文字コードで苦労したのに、世界には独自の文字セットがもっとたくさんある。どんすんの?
→全ての文字を収容した文字コード体系を作りましょう。それが Unicode

ちなみによく聞く UTF-8 は、Unicode を符号化する方法。