jyanjayakaの日記

はやめのリリース、しょっちゅうリリース

オブジェクト指向プログラミングとは何か

プログラムとはそもそも何か。*1

 

プログラムとは、何らかの方法で保管されたデータを、何らかの方法で操作・加工することである

 

a→b→c→...→x

 

入力したaというデータを加工していって、最終的にxというデータを得る。aからxを得るのがプログラムの目的である。*2

 

オブジェクト指向プログラミング(OOP)以前のプログラマは、二つの指揮を同時に行なっていた:

  1. データ加工i→jが具体的にどんなプロセスで行われるべきか
  2. aからxに到るまでのデータ加工の工程がどうあるべきか

厄介なのは1と2は相互作用していることだ。データ構造や加工の方法を変更すると、それが工程にも影響を与えるし、逆もまた然りである。

二つの変数があると問題は難しくなる。そこでよく採られる常套手段は、どちらか一方を固定するというものである。それがOOPのアイデアに他ならない。

 

OOP以後のプログラマは、二つの指揮を個別に行えばよくなった:

  1. オブジェクトの中身の設計
  2. オブジェクト間の情報の流れの制御

要はプログラムをオブジェクトの内と外に分けて、困難を分割したのである。このオブジェクトの内と外に分けるというのが、カプセル化という概念が表している意味である。

2の作業、つまりデータの流れ (stream) の制御を行なっているとき、プログラマは1について知る必要がない。知るべきなのは単にiというデータがjに加工されるという事実のみである。具体的にどうやってi→jが実現されるのかは(2の作業中は)どうでもよい。このことを指して、オブジェクトはブラックボックスなどと言われたりもする。つまりオブジェクトがブラックボックになって、中身が見えなくなってしまっても一向に構わないということだ。

オブジェクトがカプセル化され、中身が見えなくなったことによって、プログラマは2の作業に専念出来る。また、他人が作ったオブジェクトの中身をわざわざ理解しなくても、「iを入れたらjが出る」という情報(これをインターフェースと呼ぶ*3)が分かってさえいればそれが使える。こうしてプログラムの再利用も容易になる。

 

また逆に1の作業中は2を気にする必要はない。オブジェクトが持つべきインターフェース(=iを入れたらjが出る)さえ実現すれば、どんな設計をしても良い。*4*5

これらの利点はすべてコードをオブジェクトの内と外に分けるというカプセル化の概念から出てきた。したがって次が言える:

 

オブジェクト指向プログラミングの本質はカプセル化にある*6

 

OOPは上で述べたようなものだが、そこではクラスベースとか継承や多様性といった概念は登場してこなかった。なぜならクラスベースや継承や多様性といった概念はOOPの本質ではないからである。それは単にOOPをより効率的に実現するためのテクニックでしかない。しかしそれはあまりに便利であるから、OOPを行うときには必ずといって良いほど紹介される。だが、何度も繰り返すがそれはOOPの本質ではない。

 

最後に

残念ながらOOPの本質を理解しても、OOPが出来るようになるわけではない。OOPを実際に行うためには、用いる言語でOOPを行うための仕様を理解し、それを覚えて使えるようにしなければならない。それにクラスベースや継承、多様性といった概念も身につける必要がある。そのためには練習 (practice)あるのみだ。ただ練習する上でOOPのアイデアを理解しておくのはとても有用であるのは間違いない。

*1:以下では特定の言語に依らない説明を心がける。言語を決めてしまうと、その言語仕様に気が取られたり、そもそもその言語を知らないといったことが足かせになってしまう。オブジェクト指向プログラミング (OOP) はプログラム記述方法のアイデアであり、その記述方法で具体的にプログラムを書くときにやっと言語が問題になってくる。OOPの本質的アイデア自体は特定の言語を設定しなくても語れるし、そうするべきだとも思う。

*2:もちろん実際のプログラムはこのような単純な一本道とは限らない。入力がたくさんあったり、道が分岐したり、ループしたりするだろう。ただ一本道としても議論の本質は失われない。

*3:元々インターフェースというのは外界とのやりとりを司る部分という意味である。別の言い方をすれば「上っ面」だ。OOPでは物事の上っ面だけみればよいから楽なのだ。

*4:もちろん効率の良い設計が望ましいだろうが。

*5:ただもちろんその肝心のインターフェースがどんなものか知るためには、2についての理解が必要にはなるだろう。しかしそれでも1が2に影響することはない。これが肝心であり、カプセル化の利点である。

*6:カプセル化という言葉には別の使い方もあるようなので、ここでの使い方と混同しないように。

戦術T2'の実装を通じて学んだこと

プログラミング初心者が学んだこと

 

1.プログラミングの心得

まずこの本のすごさを改めて実感した。

 

 

プログラムを作る上でのモノの見方を教えてくれる。個々の技術的アドバイスよりも、こういった考え方それ自身を教えてくれる本は貴重である。

  1. 手を付けられる部分から初める。あるいは、手を付けられるよう問題を変形して小さくする。
  2. 知識は必要になった時に学べば良い

 

2.名前の重要性

変数や関数に付ける名前は、非常に重要。一目見て分かるようになるべく情報量が多く、なおかつ簡潔な名前にするとよい。良い名前の付け方については

 

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

 

 

3.データ構造とオブジェクトの重要性

データ構造が決まるとアルゴリズムが自ずと定まるというのは、大きな発見だった。

戦術T2'の実装

前回からの続き。

 

戦術T2'の実装

 

ソースコードの良い構造とは

コードはなるべく意味の単位で分割されていることが望ましい。そのためには関数を適宜定義したりするのも良いが、最も効果的なのはオブジェクトを用いる方法である。今回はまずオブジェクトを用いないでプログラミングをして、そこから逆にオブジェクトの有用性を考察する。

 

データ構造

アルゴリズムを決定する上で、データ構造が重要となる。実際、データ構造を決定するとアルゴリズムは自ずと定まる場合が多い。逆に言うとデータ構造について十分な考察をしないまま始めて、後々データ構造を変更するということになると、アルゴリズムの大部分を書き換える必要が出て来る。なのでデータ構造は予め決めておきたいところだが、難しいのはある程度アルゴリズムが決まらないとデータ構造の良し悪しが判断できないという点である。実は、この点もオブジェクトを用いれば解決できる。

 

戦術T2'はciをPiの最小数として決定する。Piのデータ構造が決まっていれば、Piの最小数を探す方法=アルゴリズムは自ずと定まってくる。それではPiのデータ構造はどうやって定めるのが良いだろうか。そのためにはPiが他にどんな操作を加えられるのか見てみるのが良い。

PiはP(i-1)からフィードバックf(i-1)によって生成される。*1要はP(i-1)の中でf(i-1)に適合する数だけを集めたのがPiである。

こういう扱われ方をするPiのデータ構造には色々な方法があるだろうが、ここでは次のようにデータ構造setを定める:

 

[[sign], num, [sign], num, [sign], num, ..., [sign], num]

 

ただしここでsignはbool型でnumは配列である。

setがどのようにしてPiを表すのか説明する。i番目のnumは自然数iを各桁毎に格納した配列である。i番目のsignはi番目のnumがPiに含まれている場合はTrueで含まれない場合はFlaseである。

 

P1はsetによって次のように表現される:

[[False], [0,0,0], [False], [0,0,1], ..., [True], [0,1,2], ..., [False], [9,9,9]]

 

データ構造setが集合Piを表す最善のものと言うつもりは全く無い。ただ採用するデータ構造がいかにアルゴリズムに影響するかを見るためには丁度よい。

*1:数学ではこのような動的な記述は嫌われるかもしれない。ただ今は具体的な手順を考察する立場なので、このような動的な記述のほうが役に立つ。

Numeron(数当てゲーム)最強アルゴリズム

ゲームの分析

ゲームの分析を通して、適切な用語や記号を導入する。

 

ゲームのルール

Numeronというゲームについての説明はここ。ゲームのルールは次の通り:*1

  • それぞれのプレイヤーが、0-9までの数字が書かれた10枚のカードのうち3枚を使って、3桁の番号を作成する。カードに重複は無いので「550」「377」といった同じ数字を2つ以上使用した番号は作れない。
  • 先攻のプレイヤーは相手の番号を推理してコールする。相手はコールされた番号と自分の番号を見比べ、コールされた番号がどの程度合っているかを発表する。数字と桁が合っていた場合は「EAT」(イート)、数字は合っているが桁は合っていない場合は「BITE」(バイト)となる。例として相手の番号が「765」・コールされた番号が「746」であった場合は、3桁のうち「7」は桁の位置が合致しているためEAT、「6」は数字自体は合っているが桁の位置が違うためBITE。EATが1つ・BITEが1つなので、「1EAT-1BITE」となる。
  • これを先攻・後攻が繰り返して行い、先に相手の番号を完全に当てきった(3桁なら3EATを相手に発表させた)プレイヤーの勝利となる。

簡単にいえば、このゲームは「数当てゲーム」である。

相手の数を推測し、そのフィードバックを得る。そしてその情報を元に新しい推測を行う。それまでに得た情報からより良い推測を行うことが鍵となる。

 

ゲームの流れ

ゲームの流れを簡潔に述べ、同時に幾つか用語を定義しておく。

  1. 二人のプレイヤーが自分の数を設定する
  2. 先攻後攻を決める
  3. 先攻のプレイヤーが相手の数を推測し発表する
  4. 先攻のプレイヤーは相手プレイヤーから推測に対するフィードバックを得る
  5. 後攻のプレイヤーが相手の数を推測し発表する
  6. 後攻のプレイヤーは相手プレイヤーから推測に対するフィードバックを得る
  7. 3へ戻る
  8. 最初に相手プレイヤーの数を当てたプレイヤーが勝者となる

 

定義1

  • ゲームをプレイする二人をp1, p2とする。p1が先攻、p2が後攻とする。
  • p1, p2が設定した自身の数をa1, a2とする。
  • プレイヤーが相手プレイヤーの数を推測し発表することをコール (call) と呼ぶ。また、piのj回目のコールで発表された数をcijと表す。
  • cijに対して得られるフィードバックをfijと表す。fijはcijとEAT, BITEの数の組みとして表す。fij = (cij, eij, bij).

 

 

ゲーム攻略の本質

このゲームに勝利する上で本質的重要性を持つのは、それまでに与えられた相手の数についての情報(それは自分がコールした数とそれに対するEATとBITEの数として与えられる)から、次にどんな推測を行うのか、その戦略である

上で定義した用語を用いると次のような問いを考えることになる:piにとってcijをそれまでに得たフィードバックの系列fi1, fi2, fi3,...,fi(j-1)からどのように定めるのが最善か。*2

 

定義2

任意のフィードバックの系列fi1, fi2, fi3,...,fi(j-1)に対して数cを対応させる写像のことをpiの戦術と呼ぶ。

 

戦術の優劣は相手の数に到達するまでのコール数の期待値で決まる。

理論的に最善の戦術を求めるのも面白そうだが、ここでは(最善とは限らない)簡単な戦術を考え、それをアルゴリズム化してpythonによって実装することを目標とする。*3

 

ゲームの簡略化

アルゴリズムを考える上で、議論を簡単にするため、ゲームを簡略化する。プレイヤーは一人(コンピュータ)であり、彼が答えの数を推測してゆくというゲームにする。このゲームの流れは次のようになる:

  1. 答えの数が決定される
  2. プレイヤーが数を推測する
  3. プレイヤーは推測に対するフィードバックを得る
  4. プレイヤーが数を推測する
  5. 答えの数が当たるまで続ける

 

定義3

  • この簡略化したゲームを1人数当てゲームと呼ぶ
  • 答えの数をaで表す
  • ゲームのプレイヤーをpで表す
  • プレイヤーがi回目のコールで発表する数をciで表す
  • ciに対して与えられるフィードバックをfiと表す。
  • ciに対して与えられるEAT, BITEの数をそれぞれei, biと表す。よってfi = (ci, ei, bi)
  • 任意のフィードバックの系列f1, f2, f3, ..., f(i-1)に対してciを対応させる写像をプレイヤーの戦術と呼び、Tで表す

 

次の問題を提起する:プレイヤーpにとって最善の戦術とは何か

ただしここでは冒頭で述べたように、この問題の解決を目指すのではなく、適当な(実装のし易い)戦術を考え、それをpythonによって実装することを目標とする。

 

道具立て

適当な戦術を考えるために便利な概念を定義する。

 

定義4

プレイヤーpがi回目のコール行う前に、答えの数として可能性のある数全体をPiと表す。

 

Piはフィードバックの系列f1, f2, f3, ..., f(i-1)によって定まるから、厳密にPiを書けばP(f1, f2, f3, ..., f(i-1))である。

 

P1は0~999までの自然数の集合と一致しない。実際、例えば000はルール上除外されるから、P1には含まれない。要はP1は0~999の数の中でルール上許される数の全体である。*4プレイヤーは最初のコールをする前であっても、答えの数についての情報「答えの数はゲームのルールに従う」を持っているわけである。

 

ゲームの流れに沿って、Pは(大抵の場合)小さくなる。P1から1回目のコールを行うことで答えの数の可能性は絞られるから  P_1 \supset P_2となる。同様に考えれば

 

 P_1 \supset P_2 \supset \dots \supset P_{i} \supset \dots

 

が成立していることが分かる。

 

幾つかの戦術

T0

最も単純な戦術はP1から予め一つ数cを選んで、常にそれをコールをし続けるものである。つまりc = c1 = c2 = ... これをT0と呼ぶ。T0は明らかに最も弱い戦術である。もしコールする数として選ぶ数が正解でなければ、永遠に答えに達することがない。T0のような戦術は理論的には考察の対象となるかもしれない(実際それはこのゲームが終了しないことがあるという一番簡単な例となっている)が、今の考察の立場からすると、考えるに値しない戦術である。

 

T1

次に考えられるのはT0を少し改善して、ciをP1からランダムに決定する戦術である。これをT1と呼ぶ。T1は明らかに弱い戦術である。実際T1はそれまでに与えられているフィードバックを全く考慮しない。与えられている情報を活用出来ていない。

 

T2

T1を改良して、ciをPiからランダムに決定する戦術をT2と呼ぶ。ここまで来ると、ある程度フィードバックの情報を活かしていると言える。しかしT2が最善の戦略と言えるかどうかはかなり疑問である。実際次のような点は考慮に値する:

  • ciをPiからランダムに決定するよりも、良い決定方法があるのではないか? 例えばPiの各要素に対して得られるP(i+1)の大きさの期待値を計算し、それが最小になるようなciを選ぶとか。
  • ciをそもそもPiから選ぶ必要もない。Piに含まれていなければ確かに答えとしてあり得ないが、可能性を絞る上ではPiのどの要素より優れているということはあり得る。

 

T2'

T2の亜種として、ciをPiの一番小さな数として決定する戦術T2'を考える。*5

T2やT2'には上で述べたような疑問点はあるにせよ、今回はT2'を実装することを目標とする。

*1:他にも追加ルールが存在するが、今回は無視する。そうしてもゲームの面白さは変わらないと思える。

*2:問題を分析し、適当な記号と用語を導入し、議論を整理する。これだけで非常に大きく前進したことになる。最初から適切な記号や用語を導入したり、問題の過不足ない分析ができるわけではない。まずとにかく分析を始めてみる。そうすると少しずつ議論でネックになる部分が分かってくる。それはよく出てくる単語や概念だったりする。次にそれに名前や記号をつける。すると議論の見通しがよくなり、少し遠くまで見通せるようになる。そしてまたネックが分かってきて...というように進んでゆく。最初から完璧にやろうと思わないことだ。地道に地盤を固めてゆくしかない。

*3:この問題を考え始めたのは数学的な動機からではなく、プログラミングを練習する上で何か良い題材がないか探していたから。

*4:|P1| = 720である。

*5:ランダムに決定するプログラムを実装するのが面倒だった。

基本のUIを作る

基本のUIを作る

ご飯記録アプリの簡単なUIを作りながらXcodeに慣れ親しもう。

 

学習目標

  • Xcodeでプロジェクトを作成出来る
  • Xcodeプロジェクトテンプレートとともに作成されるキーファイルの目的を理解する
  • プロジェクト内のファイルを開いたり切り替えたり出来る
  • iOSシミュレーターでアプリを起動できる
  • ストーリーボードでUI要素を追加、移動、リサイズ出来る
  • 属性インスペクターでストーリーボードにおけるUI要素の属性を編集できる
  • アウトラインビューでUI要素を表示したり再加工出来る
  • Assistant editor’s Preview modeを利用してストーリーボードUIをプレヴュー出来る
  • オートレイアウトを使って、アプリ使用者のデバイスサイズに自動的に適応するように、UIをレイアウト出来る。

さあ始めよう!

Start Developing iOS Apps (Swift)の超翻訳。1段落を1行に圧縮。名付けて。ALfAP = A Line for A Paragraph。広めていきたい。

 

さあ始めよう!!!

iPhoneiPadで動くアプリの作り方を学ぶ最高のレッスンだぜ。

 

最初から順番にレッスンを受ければ、最後にはシンプルだけどそれなりのiOSアプリが出来上がっているはずだ。

 

学べること

 

予備知識

Swiftがある程度分かるとbetterだね。

 

Swift基礎知識不足なら

が基礎知識を学ぶ良い方法。

 

レッスンについて

ご飯記録アプリの完成を目指す。

f:id:ziguzaku:20170826094229p:plain

 

各レッスンのXcodeプロジェクトファイルを上手く活用してくれ。

 

用語集を使って復習をしてくれ。

 

道具の入手

iOSアプリをクールに作りたいなら最新のXcodeが動くMacが必要だ。

  • Xcode=アプリを作るのに必要なモノが全部揃ってる!
  • iOS SDKiOSでの開発に特化したXcodeDLC

 

(最新のXcodeのダウンロード方法は割愛)*1

 

重要: Xcode, iOS SDK, Swiftのバージョンはインストラクターのと揃えてくれ。

 

*1:手順を1行にまとめることは性質上出来ない。まとめる意味がない。手順に素直に従えばOKだ。