« Pythonで微分積分や統計の基礎を理解しよう | トップページ | 第22回東京Redmine勉強会の感想 #redmineT »

2022/05/26

「コーディングを支える技術」は良い本だ

コーディングを支える技術 ~成り立ちから学ぶプログラミング作法 (WEB+DB PRESS plus)」を読んで、良い本と思った。
ラフなメモ書き。
プログラミング初心者の適当な感想。

【参考】
コーディングを支える技術――成り立ちから学ぶプログラミング作法:書籍案内|技術評論社

「コーディングを支える技術」著者公式ページ
「コーディングを支える技術」著者公式ページ 補足記事

【1】Javaで開発経験をした後、RubyやPythonを覚えた今、「コーディングを支える技術 ~成り立ちから学ぶプログラミング作法 (WEB+DB PRESS plus)」を読むと、ああそういうことか、と気づくことがある。

【2】IF文の裏ではGOTO文が動いている、という内容を読んで、VBのことを思い出した。
VBでは、例外処理などのAPIが貧弱なので、割とGOTO文がよく出てくるので、VBは好きでなかった。
たぶん、APIが貧弱な古いプログラミング言語ほど、GOTO文が幅を利かせている。
その分、何でもできるが、とても読みづらく保守しづらい。

コーディングを支える技術 ~成り立ちから学ぶプログラミング作法 (WEB+DB PRESS plus)」の一節に、大学でC言語を初めて習った人が、「while文があればfor文はいらないのでは?」と思ったらしいと書かれていた。
僕も似たような感覚を抱いていた時があった。
JavaでもRubyでも、なぜ、while文、do-while文、for文、拡張for文など、似たようなループ処理の文法があるのか?と不思議に思っていた。

理由は単純で、当初はIF文みたいにwhile文を使っていたが、カウンタ変数のスコープがブロック外にあるので使いにくいから。
だから、while文からfor文の記法により、カウンタ変数のスコープはブロック内に収まった。
さらに、拡張for文により、カウンタ変数そのものも不要になった。
さらに、ListのforEachメソッド、Rubyのブロックのように、for文はクロージャとして吸収される。
つまり、プログラミングの文法もどんどん進化しているわけだ。

【3】変数のスコープの説明では、Perlで静的・動的スコープをプログラムで説明してくれている。
Perlを初めて書いていた時、なぜlocalやmyを変数に付ける必要があるのか、分からなかった時を思い出した。

Pythonでは、わざわざglobal/nonlocalで変数のスコープを制御できるようにしてくれている。
この文法を見た時、Perlみたいだな、と思ったことを思い出した。

Rubyなら、def~endのブロックとクロージャのブロックで、変数のスコープが異なるように使い分けている。
この文法は最初は理解しづらかったが、このおかげで、Rubyはメタプログラミングでやり放題ができるわけだ。

【4】オブジェクトとクラスでは、JavaとRubyでは考え方が全く違う。
親子で継承関係にあるクラスがあった時、インスタンス変数、インスタンスメソッド、クラス変数、クラスメソッドの挙動がJavaとRubyで異なる。
Javaでは、インスタンス変数、クラス変数、インスタンスメソッド、クラスメソッドは、親子クラスそれぞれで保持する。
ただし、親クラスの変数に、子クラスのオブジェクトを代入した場合、インスタンスメソッドは、子クラスのメソッドを利用する。
つまり、Javaでは、親クラスの変数の振る舞いは、ポリモーフィズムで動的に処理が変わるように見せかけられる。

一方、Rubyでは、親子間でインスタンス変数が共通として扱われる。
親クラスが定義したインスタンス変数と同じ名前のインスタンス変数を子クラスで使うと、親クラスのインスタンス変数は子クラスで上書きされる。
この書き方に慣れると、「クラス(型)は仕様である」を時々忘れてしまう。

資格取得に40代後半からチャレンジ JavaとRubyを比較(注意しておくことその1)

資格取得に40代後半からチャレンジ JavaとRubyを比較(注意しておくことその2)

資格取得に40代後半からチャレンジ JavaとRubyを比較(注意しておくことその3)

資格取得に40代後半からチャレンジ JavaとRubyを比較(注意しておくことその4)

Javaでは「クラス(型)は仕様である」が、Rubyではメッセージを渡したオブジェクトに全てを委ねる。
Rubyはまさにダック・タイピングなので、例えばdesktop.printが変数なのか、メソッドなのかも動かしてみないと分からない。

【5】多重継承のデメリットをどう解決するか?も、Java、Ruby、Pythonなどで全く違う。
Javaは多重継承を禁止するが、インターフェイスを導入して解決しようとした。
Javaでは、継承の代わりに委譲を進めるが、さらに委譲の参照をソースにハードコードするのではなく、依存性の注入により、設定ファイルを使って動的に変更できるような手法が多くなった。
JavaのフレームワークではDIが多いから、設定ファイルにXMLを用いることが多く、その分、プログラムを書くよりもXMLを書いている時が多くてあまり楽しくなくなる。

Rubyは、多重継承の問題を「どの順番で継承関係を検索するか」という問題に置き換えた。
Rubyでは継承関係は、Objectを頂点とするツリー構造だから、継承ツリーをメソッド検索することで解決しようとする。
include, prepend, refinementsはそういう使い道だろう。
また、Rubyでは、どうしても多重継承したい時は、モジュールでMix-inを使う。

Pythonでは多重継承できる、と聞いていてまだ理解できていなかったが、「コーディングを支える技術 ~成り立ちから学ぶプログラミング作法 (WEB+DB PRESS plus)」によれば、C3線形化のアルゴリズムで継承関係の検索を決めることで解決しているわけだ。

[Python入門]多重継承:Python入門(1/2 ページ) - @IT

Pythonのメソッド解決順序(MRO) - 情報系大学院生の勉強メモ

【6】それから、並行処理のプログラミングの章も非常に分かりやすい。
Windows3.1やMacOS9は強調マルチタスクで、プリエンプティブマルチタスクではなかった、という一節を読んだ時、昔のPowerbookがMacOS9だった時を思い出した。

並行処理の問題点は、競合状態が起きること。
競合状態は3つある。
1つ目は、2つの処理が変数を共有している。
2つ目は、少なくとも1つの処理がその変数を上書きする。
3つ目は、一方の処理が一段落付く前に、もう一方の処理が割り込む可能性がある。

1つ目の対策は、結局スレッドを使っているので、変数を共有しない方向で解決していない。
アクターモデルのように、メッセージを送ることで解決しようとしている。
Erlangがその例だろう。

2つ目の対策は、メモリを共有しても書き換えないようにする方針もある。
全ての変数はImmutableにすればいい。
Haskellなどの関数型言語がそうだろう。

3つ目の対策は、協調的なスレッドを使う。
たとえば、Rubyのファイバー、Cのコルーチン、Pythonのジェネレータ。

あるいは、ロック、ミューテックス、セマフォ、モニタのような機構を使う。
Javaなら、synchronizedブロックで囲むだけでいい。

【7】自分は「プログラミングのできない山羊」出身なので、プログラミングを経験してやっとこういう本の内容が分かってきたという気がしている。
ソフトウェア開発は、数学や物理、経済学、財務会計、心理学などと違って、やっぱり独特の考え方がいると思う。
自分の中で言語化できていない部分は気づいたらブログで残しておく。

「プログラミングのできる羊とできない山羊を区別する方法」の記事のリンク: プログラマの思索

「60%の人間はプログラミングの素質がない」記事のリンク: プログラマの思索

プログラミングしてる時はでっかいピタゴラ装置を作ってるみたいな感じ: プログラマの思索

プログラミングは「ブロックを組み合わせる」感覚に似ている: プログラマの思索

|

« Pythonで微分積分や統計の基礎を理解しよう | トップページ | 第22回東京Redmine勉強会の感想 #redmineT »

IT本」カテゴリの記事

プログラミング」カテゴリの記事

ソフトウェア工学」カテゴリの記事

Ruby」カテゴリの記事

Python」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



« Pythonで微分積分や統計の基礎を理解しよう | トップページ | 第22回東京Redmine勉強会の感想 #redmineT »