だいたいきままなエンジニア志望のブログ

エンジニア志望の大学院生の考えたことあれこれ

プログラミング言語の歴史から掴むオブジェクト指向の考え方

オブジェクト指向,なんとなく学んだことはあっても完全に理解できていなぁと感じていたので参考書をよみながらまとめてみました.
プログラミング言語の生い立ちとともにオブジェクト指向の考え方を追っていきます.

黎明期,機械語によるプログラミング

コンピュータは0,1の2進数で書かれた機械語しか解釈することはできません.したがって1940年代にはプログラマ機械語を使ってプログラミングをしていました.
たとえばこんな感じ.(16進数表現による機械語プログラム)

A10010
8B160210
01D0
A10410

まったくもって読めない...

アセンブリ言語

次に登場した言語がアセンブリ言語です.無機質な機械語を人間にわかりやすいよう記号に置き換えて表現することが特徴です.

MOV AX, X
MOV DX, Y
ADD AX, DX
MOV Z, AX

MOVとかADDとか命令っぽいものが確認できます.

高級言語

アセンブリ言語から更に進化し,人間により親しみやすい表現形式を追求して高級言語が発明されました.
FORTRANCOBOLがはじめに登場しました.
さきほどから例に上げているプログラムをFORTRANで書くと以下の通り,

Z = X+Y

我々プログラマには馴染みのある形になりましたね.
変数XとYの足し算の結果をZに格納するという,今までと比べると格段にわかりやすい形式になっています.

構造化プログラミング

「正しく動作するプログラムを作成するためにはわかりやすい構造にする事が重要」という考え方が構造化プログラミングです.
構造化プログラミングはロジックを以下の3つの構造だけで表現することを提唱しています.

  • 順次実行
  • 条件分岐
  • 繰り返し

f:id:arbol962:20190220175717p:plain
構造化プログラミングの三要素

  • 順番に処理は実行されるよ
  • 条件で処理を変えることができるよ
  • 条件のあいだ,処理を繰り返すことができるよ

おなじみの三要素ですね.

プログラミング言語は保守,再利用性重視に

時代が進み,ハードウェア性能が向上していくと従来に比べてプログラム自体のサイズ,実行速度の制約がゆるくなり,わかりやすいプログラムを記述することが重要視されるようになりました.

サブルーチンの独立性を高める

保守性を高めるにはサブルーチンの独立性を高めることが重要です.
理解のためにまず用語の定義

  • サブルーチン:プログラムに複数回現れる同じ命令を一箇所にまとめたもの
  • グローバル変数:複数のサブルーチンが編集できる変数

ここで,グローバル変数にエラーが出てしまうと修正するためにすべてのサブルーチンをチェックしなければなりません.
これってとても非効率ですよね.
メインの処理とサブルーチンの処理をできるだけ分離(グローバル変数を無くすこと)できれば,エラー箇所のみのチェックで済むわけです.
サブルーチンの独立性を高めるって大事ですね.

サブルーチンの独立性を高めるために考え出されたのがローカル変数値渡しです.

  • ローカル変数: サブルーチンの中でのみ使用できる変数.
  • 値渡し : サブルーチンにそこで使用する値(だけ)を渡すこと
グローバル変数問題と貧弱な再利用性(サブルーチン)

ローカル変数はサブルーチンが終わると存在自体が消滅してしまいます.したがってサブルーチンの前後で保持する必要がある情報はグローバル変数として保持するしか方法はありませんでした.グローバル変数はエラー修正のコストを高める厄介な存在ですが,従来の構造化言語ではこのグローバル変数問題を避けることは困難でした.
また,再利用できる共通部品として用意されているサブルーチンのみであり,大規模な共通部品として扱える仕組みは存在していませんでした.
これらの問題を解決するものこそオブジェクト指向なのです.


オブジェクト指向の特徴

これらを踏まえてオブジェクト指向の特徴を見ていきましょう.
ざっくり乱暴にまとめるとオブジェクト指向は以下の仕組みを持ちます

  • サブルーチン以外の再利用を可能にする仕組み


今までのプログラミング言語の問題点を解決するために編み出された考え方だということがよくわかります.
では改めてかの有名なオブジェクト指向の三大要素を見ていきましょう.

オブジェクト指向の三大要素

サブルーチンと変数をまとめて部品を作る.メインルーチン部分からは変数にアクセスできない
データや処理を外部から直接利用できなくすること.
外から触れることができなければプログラムが壊れる心配がない.プログラムが壊れにくくなるための機能

メソッドの呼び出し側を共通化する
クラスによって同一のメソッドで違う処理が行える
複数のメソッドを呼び出すロジックを一本化する.
逆に言うと異なるクラスのメソッドでも同じ名前で呼び出せる.

  • 継承

重複するクラス定義を共通化
同じ定義を二回目以降は書かなくていい.
継承によってクラスの共通部分の記述を省略,相違点だけ新たに記述すれば良い



プログラミングの歴史を感じる...(小並感
今回,利用した参考書はこちらです

オブジェクト指向でなぜつくるのか 第2版

オブジェクト指向でなぜつくるのか 第2版

難しいこと考えずKotlinでJSONをパース

Kotlinを使ってJSONをパースしてみました.

今回はWikipedia APIのデータをパースして「記念日・年中行事」のデータを取得します.

WikipediaAPIを叩いてデータを用意

日付を指定してWikipedia APIを叩きます.
https://ja.wikipedia.org/w/api.php?format=json&utf8&action=query&prop=revisions&rvprop=content&titles=4月1日
とすると以下の構造のデータが得られます.

{
   "batchcomplete": "",
   "warnings": {},
   "main": {},
   "query": {
        "pages": {
             "2004": {
                  "pageid": 2004,
	          "ns": 0,
                  "title": "2月18日",
                  "revisions": [
		       {
			"contentformat": "text/x-wiki",
			"contentmodel": "wikitext",
			"*": "{{カレンダー 4月}}\n'''4月1日'''(しがつついたち)は、[[グレゴリオ暦]]で年始から91日目([[閏年]]では92日目)にあたり
		       }
		  ]
	      }
          }
    }
}

パースしたい階層

今回のターゲットは
"query"."pages"."2004"."revisions".[0].["*"]
に記述されている本文です.


ここで"2004"はpageidであり,日毎に変わるキー名となることに注意してください.

(4月1日の記事におけるpageidは2004)


本来ならばJSONの形式に沿ったクラスを用意して変換するべきなのですが,今回は力技でパースすることにします.

Kotlinでパース

// wikiResponse:wikipediaAPIのレスポンス(String型)
val parentJsonObj = JSONObject(wikiResponse)

val queryObj = parentJsonObj.getJSONObject("query")

val pagesObj = queryObj.getJSONObject("pages")

// ページidは記事によって異なるのでpagesObjectの一番目のキーとして名前を取得
val pageNumJsonObj = JSONObject(pagesObj.getString(pagesObj.keys().next().toString()))

// revisionsの次はキー名が存在しない.次の階層はインデックスで指定するJsonArrayに変換
val revisionsJsonArray = pageNumJsonObj.getJSONArray("revisions")

val zeroJsonObj = revisionsJsonArray.getJSONObject(0)

var today_article = zeroJsonObj.getString("*")

これで記事の本文を取得することができます.


取得した記事のノイズ取りや整形

本文の内容は以下の記事によって構成されています
== できごと ==
== 誕生日 ==
== 忌日 ==
== 記念日・年中行事 ==
== フィクションのできごと ==
== 誕生日(フィクション) ===
== 出典 ==
== 関連項目 ==

今回は記念日の部分を取得したいのでsubstringで記念日・年中行事とフィクションの出来事の間を切り抜いてあげるといいでしょう

var today_anniv = today_article.substring( today_anniv.indexOf("==記念日・年中行事=="), today_article.indexOf("==フィクションのできごと==") )

とすればOK

ただこのままだと区切り記号 [ ] や,記念日の国情報( {{JPN}}や{{USA}} )も乗っているので綺麗にしたい場合は

today_anniv.replace("[", "")
today_anniv.replace("]", "")
today_anniv.replace("""\{\{...\}\}""".toRegex(), "")

とするといいかもしれません.

node.jsのREPLで変数宣言すると出力されるUndefinedは何者か

ふとJavascriptを触ってみようと思い立ち,node.jsを入れてみました.
そのときに少し気になったことがあったのでメモ

変数宣言時,謎のundefined

node.jsの対話環境であるREPLを起動して適当な変数を宣言してみると,「undefined」という見慣れない出力が。

> var test = "abc";
undefined
> test
'abc'

宣言に失敗したのかとも思いましたが変数は宣言できている様子。
宣言するたびに出てくるこいつは一体何なんだ...

正体は「文の結果」

この記事が参考になりました。
https://qiita.com/uhyo/items/39750a5359629b3fee05qiita.com

ざっくりまとめると

  • Javascriptは文の結果という概念を持っている (1+2なら結果は3)
  • 実行するとその文の結果が保持される
  • 結果が得られないような場合( var x, for文など)はEmpty(結果なし)となる。最終結果がEmptyの場合undefinedに変換される
  • 対話環境であるREPLだとこの文の結果が出力として表示される



ということらしいです。
つまり出力されていたundefinedは変数宣言でEmpty -> undefinedとなった文の結果だったんですね。
Javascriptを触り始めて,すぐ言語仕様までたどることになるとは思いませんでした!言語仕様の世界奥深い...


キーワード: JavaScript, REPL, Undefined, 関数宣言

ブログ,はじめました

「エンジニアとして時代に取り起こされないように」

「日々の技術進歩を記録しておくアウトプットの場」

エンジニアがブログを書くモチベーションはいろいろありますよね .

このブログではそんな高尚な思想は(あんまり)存在しません.あくまで一人のエンジニア志望があれこれ試して考えたこと,ハマったことの供養場所として設定しています.

願わくば同じことで誰かが悩まないように...

 

...なるといいですね!!!!!