社内se × プログラマ × ビッグデータ

プログラミングなどITに興味があります。

文字コードの歴史を少し知りたい

python のドキュメントですが、文字コードの歴史について書かれている部分がありました。
https://docs.python.org/2.7/howto/unicode.html

文字コードと言えば、何も考えずに "UTF-8" でみたいなところがあって、正直よく分かっていません。

歴史
ドキュメントによると、1968年に ASCII コードが標準化されたとあります。

アメリカの標準だったということで、普通のアルファベットのみが定義されていて、アクセント付きの文字( ‘é’ or ‘Í’)については、定義されていなかったとのこと。

1980年代になっても、ほとんどのパーソナルコンピューターは 8bit だったため、0 から 255 までの値しか扱えない。
その中でも、ASCII コードは 127 までしか使っていなかったので、残りの 128 から 255 までを使って、アクセント付きの文字などを補おうと頑張っていた様子。
とはいえ、世界各国の文字を補うにはあまりにも少なすぎるし、独自仕様が出来すぎてしまったので、Unicode 標準化という動きが始まったらしい。

Unicode では、16bit で文字を取り扱うことを始めたので、2^16 = 65,536 の値が使用できることになる。
当初はこれで、全ての言語のアルファベットを扱うことを目標にしていたみたいですが、後になって足りないことが発覚。
そして Unicode では、0 から 1,114,111 の値(どのように算出しているかは不明ですが)が使用できるようにしたらしい。

Unicodeとは
世界中の文字の集合。一応、それぞれの文字に対して番号は振られているみたいですが、それ自体はコンピュータが解釈する数値ではないようです。
平仮名の ”あ” だったら、U+3042。
符号化文字集合とも言われているようですが、あまり覚えていません。
これらの文字をパソコンで扱うためには、「符号化形式」にしたがって、文字を数値化する必要がある。
その「符号化形式」と呼ばれるものが Shift-JIS や UTF-8 になる。
Unicode = UTF-16 みたいなことを情報処理試験対策で学んだ気もしますが、この解釈だと違うようです

UTF-8とは
ASCIIコードの文字に、世界中の文字を加えた文字コード
ASCIIで定義している文字を、Unicodeでそのまま使用することを目的としているので、互換性が高いようです。
ASCIIと同じ部分は1バイトで表現し、そのほかの部分を 2~6バイトで表現する可変長の符号化方式なので、漢字や仮名文字(3〜4バイト)などはデータサイズが少し大きくなるようです。

互換性が高く世界中の色んなアプリケーションが採用しているので、とりあえず UTF-8 を基準に考えるのは当然のことかなと思います。

Java インターフェース 実装してみる

前回の記事で、インターフェースのメリットが解った?ところで、実装をしてみます。
blueskyarea.hatenablog.com

今回は、データベースからデータを取り出すところに、Dao インターフェースを実装します。
インターフェースの定義
Dao をインターフェースを定義します。
データベースから全てのデータを取得するための、getAll() メソッドを定義しています。

public interface Dao {
	Map<Integer, String> getAll();
}
MySqlDao の作成

例としてMaySql からデータを取得するとして、MySqlDao を作成します。
Dao インターフェースを実装するため、getAll() の実装が強制されます。
もちろん戻り値の型も インターフェース側で定義したものと同じ Map でなければなりません。
これは MySqlDao の実装担当者が、意図しない型でデータを返却することを防いでくれます。

public class MySqlDao implements Dao {
	@Override
	public Map<Integer, String> getAll() {
		Map<Integer, String> map = new HashMap<>();
		map.put(1, "mysql1");
		map.put(2, "mysql2");
		map.put(3, "mysql3");
		return map;
	}
}

※今回は例なので、単に固定値を返しているだけです

クライアント の作成

MySqlDao を実際に使うクライアントを作ります。

public class Client {
	public static void main(String[] args) {
		Dao dao = new MySqlDao();
		new Client().getData(dao);
	}
	
	private void getData(Dao dao) {
		dao.getAll().forEach((key, value) -> {
			System.out.println(key + ":" + value);
		});
	}
}
実行1
1:mysql1
2:mysql2
3:mysql3

このように期待した値が得られています。

MySql -> Postgres への変更

さてここで、データソースを MySql から Postgres に変更することになりました。
もちろんクライアント側は、同じ出力結果を期待しています。
まず、Postgres からデータを取得するための PostgresDao を作成します。
同じく Dao を実装しているので、getAll() の実装が強制されます。
クライアント側は、MySql の時と同じ出力結果を期待しているので、これは好都合です。

public class PostgresDao implements Dao {
	@Override
	public Map<Integer, String> getAll() {
		Map<Integer, String> map = new HashMap<>();
		map.put(1, "postgres1");
		map.put(2, "postgres2");
		map.put(3, "postgres3");
		return map;
	}
}
クライアント側で使う Dao を変更

あとは、クライアント側で使う Dao を MySql から Postgres に変更してあげます。
以下のように、変更したのは、生成する dao インスタンスだけです。

public class Client {
	public static void main(String[] args) {
		//Dao dao = new MySqlDao();
		Dao dao = new PostgresDao();  // 変更したのはここだけ
		new Client().getData(dao);
	}
	
	private void getData(Dao dao) {
		dao.getAll().forEach((key, value) -> {
			System.out.println(key + ":" + value);
		});
	}
}
実行2
1:postgres1
2:postgres2
3:postgres3

このようにクライアント側では、使用する Dao を変更しただけで、期待する結果を得ることができました。
これは利用者側にとって好都合です。
利用者側は、Dao がどのようにデータをデータベースから取得しているかなどは意識する必要がありません。
使いたいデータベースの Dao を使うだけで、期待する値が得られます。

感想

個人的に同じシステムにおいて、データソースを途中で変更するというケースに遭遇したことがないので、この恩恵を実際に得られたことはないです。
ただ、インターフェースを使うことによって、疎結合を実現できれば、変更に強いシステムになり得ることは理解できました。

Java インターフェース メリット わからない

もう何度も実装したことがあるにも関わらず、そのメリットがいまいちピンときていなかったりします。
ネットで検索すると、メリットについて語っている色々な情報が出てきます。
その時は何となく理解できた気になるのですが、しばらくするとまたアレなんだっけ?という疑問がでてくるループに入っています。

1. インターフェースとは、処理は具体的に書かれていない、メソッドの型だけを宣言したもの

インターフェースがどういうものかは分かりますが、これだけではメリットについては何も分からないです。
処理を具体的に書かないと、何の役にも立たないのに、何でそんなことをするのか?という疑問が残ります。。

2. 処理内容を具体的に書いていないから、後で変える事ができる

なぜ、処理内容を後で変える事ができるとうれしいのでしょうか。
結局実装するのだから、それを後回しにしているだけのようにも聞こえますが、それがメリットなのでしょうか。。

3. インターフェースを実装したクラスではメソッドの実装が必須だ

そのメソッドを実装することを強制することができるのは、実装漏れを防ぐというメリットがあるのでしょうか。。
何れにしても、欲しいメソッドを実装していなければ処理は動かないのだから、実装漏れすることは無いように思います。

4. 処理は別で記述する方が、後々の変更などが予想される場合には便利

後々の変更というよりは、新しいクラスを追加し、それに実装するという意味かなと思います。
それが便利なのでしょうか。。

5. インターフェースは多重継承のように複数のインタフェースを実装可能

複数のインターフェースを実装するということは、それだけ実装しなければいけないメソッドが増えることになります。
実際に複数のインターフェースを実装するとややこしいし、これをメリットと捉えることができるのでしょうか。。

6. 利用者側へこちらが指定したインターフェースの使用を要求する

内容的には、”インターフェースを実装したクラスではメソッドの実装が必須” と同じことを言っています。
ただ、こちらの表現の方が利用者側に対して何らかの実装を要求する(強制させる)という意味合いが理解しやすいです。

7. Runnable インターフェースを例に考えてみる

Java でスレッド処理を書くときに出てくる Runnable インターフェースです。

private static class MyThread implements Runnable {
    @Override
    public void run() {
        // 何かの処理
    }
}

Runnable インターフェースを実装したクラスは、run() メソッドを実装することを利用者に強制することができます。
run() メソッドに書く処理内容は利用者側に一任されており、好きな処理を書くことができます。
利用者側は以下のように Thread のインスタンスを生成することで、簡単にスレッドを作ることができます。

Thread t1 = new Thread(new MyThread());
t1.start();

もし、MyThread とは違う処理をするスレッド(MyThread2 )を作りたくなったとします。

private static class MyThread2 implements Runnable {
    @Override
    public void run() {
        // 何かの処理
    }
}

この場合も同じく利用者側に要求されるのは、run() メソッドの実装です。
逆に言うと、それ以外には利用者側に要求されていません。
同じく run() メソッドに処理を実装することで、異なる処理を持つスレッドを作ることができます。

Thread t2 = new Thread(new MyThread2());
t2.start();

要はインターフェースが同じなので、利用者側に実装を要求されるメソッドも同じものになります。

この観点からすると、インターフェースは利用者側にとってメリットがあるように思えます。
利用者側がその機能を利用するためには、何を実装する必要があるのかが、明示されているからです。

8. インターフェースで疎結合を実現することができる

疎結合、すなわち各クラス間での依存性を低くすることで、変更に強いアプリケーションを作ることができるという意味かと思います。
以下の例が分かりやすかったです。
悪い例:

public class PC {
    public void setKeyboard(Keyboard keyboard) {};
    public void setMouse(Mouse mouse) {};
}
public class Keyboard {}
public class Mouse {}

良い例:

public class PC {
    public void setUsbDevice1(USB usb) {};
    public void setUsbDevice2(USB usb) {};
    public void setUsbDevice3(USB usb) {};
}
public class Keyboard implements USB {}
public class Mouse implements USB {}

悪い例の方は、PC クラスが Keyboard クラスと Mouse クラスに依存してしまっていて、仮に Keyboard クラスが削除された場合、PC クラスも修正しなければならないし、もし Printer クラスが追加された場合、PC クラスも修正しなければならない。
良い例の方は、USB というインターフェースを介しているので、仮に Keyboard クラスが削除された場合でも、PCクラスに修正は発生しないし、もし Printer クラスが追加された場合、USB インターフェースを実装すれば、PC クラスを修正することなく、既存のメソッド setUsbDevice[1-3] を利用することができます。

なるほど、具体的な例を見てみると、より納得感があるように思います。
今のところ、以下の2点がメリットと思えてきました。
1.利用者側に必要な実装を明示できること
2.クラス間の疎結合を実現できること

基本的に利用者側にメリットがありそうです。
あとは、実際にこれらを意識して、設計・プログラミングができるかどうか。1はまだしも、2がやや難しそうです。

Python 言語について 新しく知った 9つのこと

Python の初心者です。
Pythonによるデータ分析入門(第二版) の第二章に Python 言語について書かれていた内容から、自分にとって新しい発見が多かったので、それを列挙します。
既に Python を学んだことがある方にとっては、常識的な内容かと思います。

1. インデントは 4つの空白で
自分は 2つの空白でよく書いてますが、4つがスタンダードらしいです。

2. セミコロンを使えば、複数の変数を1行で定義できる

a = 5; b = 6; c = 7

Java のように各行の最後に ; を置く必要はないですが、あえて置くことで、このような定義が可能になります。
しかし読みづらいので、特に推奨はされないですね。

3. すべてのものがオブジェクトである
Python の開発者が好きな言葉らしいですが、これが意味することについては、まだ理解できていないです。
恐らく、以下の記事を読んだほうが理解が深まりそうです。
http://blog.lerner.co.il/pythons-objects-and-classes-a-visual-guide/

4. リストの場合 a = b は値のコピーではなく、参照先のコピーである

a = [1, 2, 3]
b = a
a.append(4)
b
# -> [1, 2, 3, 4]

a の値を変更すると、b の値も変更されている。
すなわち a と b が参照している値は同じである。
リストの場合だけではないかもしれませんが、とりえあず。

5. 関数に渡す引数がリストの場合も値のコピーではなく、参照先のコピーである

def append_element(list, element):
    list.append(element)

data = [1, 2, 3]
append_element(data, 4)
data
[1, 2, 3, 4]

data を append_element に渡し、関数内で演算後、呼び出し元で data の値を見てみると、関数内で演算した内容が反映されている。
すなわち 関数に渡した data と元の data が参照している値は同じである。
こちらもリストの場合だけではないかもしれませんが、とりえあず。

6. 暗黙の型変換は明らかな場合のみ

a = 4.5
b = 2
a / b
# -> 2.25

float と int の計算時には暗黙的に変換してくれます。

a = 2
b = "m"
print(a + b)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-41-827396fae8f4> in <module>()
      1 a = 2
      2 b = "m"
----> 3 print(a + b)

TypeError: unsupported operand type(s) for +: 'int' and 'str'

int と str をつなげようとしても、エラーになります。
Java などでは、自動的に int が str に変換されて処理してくれますが、python ではより厳密に型について考えられているようです。

7. isinstance(value, type) で型のチェックができる

a = 5
isinstance(a, int)
# -> True

a = 5
isinstance(a, str)
# -> False

何の型かを調べるなら、type(a) で "int" が得られます。

8. Duck typing
“If it walks like a duck and quacks like a duck, then it’s a duck.”
"もしそれがアヒルのように歩き鳴くのであれば、それはアヒルだ。"
以下の例で言うと、、

def isiterable(obj):
    try:
        iter(obj)
        return True
    except TypeError: # not iterable
        return False

もし、そのオブジェクトからイテレータを作成できるのであれば、それは iterable である。
当然と言えばそうかもしれませんが、あるオブジェクトの型そのものは気にしなくても、その特性について意識する場合、こういったチェックが必要になってくるのだろうと思います。
"Duck typing" について、何となく分かったような気もしますが、まだぼんやりとしています。

9. 値の比較は ==, オブジェクトの比較は is

a = [1, 2, 3]
b = a
b == a # True
b is a # True

b と a は値も参照先も等しい。

a = [1, 2, 3]
c = list(a)
c == a # True
c is a # False

c と a は値は等しいが、参照先は異なる。

所感
細かい点を含めると、一節を読んだだけでも知らないことがたくさんでてきますね。
また、読んだときには分かったような気になってても、忘れてしまうことも多いので、繰り返し練習しながら理解を深めていきたい。

Java シングルトンデザインパターン singleton design pattern

singleton design pattern 実装する機会は今までに何度もあったものの、改めて順序だてて考えてみます。
”そのクラスのインスタンスは常に1つしか存在しないことを保証する”こと。

実装にあたって、気をつけたい点

1. どのようにして、インスタンス化を行うのか
2. どのようにして、そのインスタンスが一つだけであることを保証するのか
3. どのようにして、そのシングルのインスタンスを利用できるのか

実装

1. コンフィグファイルを読むためのクラスをシングルトンにしてみます

public class Config {
	
	private static Config config = new Config();
	
	private Config(){}
	
	public static Config getInstance() {
		return config;
	}
	
	public String getString(String key) {
		return "example";
	}
}

ポイント
1. コンストラクタが private なので、外部からこのインスタンスを生成することはできない
2. config のインスタンスが static で生成されているため、メモリ上に1度しか割り当てされない
> この Config クラスがロードしたときだけ、インスタンスが生成される
> スレッドセーフである

欠点
コンストラクタにパラメータを与えることができないので、柔軟性に欠ける

2. 上記の欠点を補うため、getInstance() 内でコンストラクタを生成するようにしてみます

public class Config {
	
	private static Config config;
	private String pamameter;
	
	private Config(String parameter){
		this.pamameter = parameter;
	}
	
	public static Config getInstance(String parameter) {
		if (config == null) {
			config = new Config(parameter);
		}
		return config;
	}
	
	public String getString(String key) {
		return "example" + pamameter;
	}
}

ポイント
1. getInstance がパラメータを受け取り、それをコンストラクタに渡せるようになった
2. config が null の時だけ、インスタンスを生成しようとしている

欠点
スレッドセーフではない

3. 上記の欠点を補うため、getInstance() を synchronize にしてみます

public class Config {
	
	private static Config config;
	private String pamameter;
	
	private Config(String parameter){
		this.pamameter = parameter;
	}
	
	public synchronized static Config getInstance(String parameter) {
		if (config == null) {
			config = new Config(parameter);
		}
		return config;
	}
	
	public String getString(String key) {
		return "example" + pamameter;
	}
}

ポイント
getInstance() を synchronized にしたので、マルチスレッド下でも重複してインスタンスが生成されない

欠点
synchronized が必要なのはインスタンスが生成されるまでだが、getInstance() が呼ばれる度に判定されてしまう
> 不要なオーバーヘッド

かなり頻繁に getInstance() が呼ばれるケースでは、パフォーマンス面で問題になるかもしれませんね。
上記の欠点については、別の機会に考えます。

いまさら IPython を初めて使ってみる

いまさらですが、IPython は pythonインタラクティブシェルを拡張したもので、タブ補完機能などが使えるみたいです。
Python for Data Analysis の本を読む機会があって、そこで初めて試してみました。

起動
ipython コマンドで起動可能です。
実はインストールした覚えがないのですが、恐らく Anaconda を使って python インストールした時に、一緒に入ったのではないかと思います。
Python 3.6.3 に対して、IPython はもう 6.1.0 ですか。

$ ipython
Python 3.6.3 |Anaconda, Inc.| (default, Oct 13 2017, 12:02:49) 
Type 'copyright', 'credits' or 'license' for more information
IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.

試行
とりあえず、試してみます。
最近、python2 に慣れているので、単純に文法ミス。。

In [1]: a = 5

In [2]: print a
  File "<ipython-input-2-9d7b17ad5387>", line 1
    print a
          ^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(a)?

通常のインタラクティブシェルと同様に変数だけ指定しても出力してくれます。
その場合、In に対して Out が分かり易く表示されています。

In [3]: print(a)
5

In [4]: a
Out[4]: 5

文字列の場合。

In [24]: greeting = "hello ipython!!"
In [25]: greeting
Out[25]: 'hello ipython!!'

%run ファイル名 を指定することで、ローカルにあるスクリプトファイルを実行することも可能です。

In [23]: %run test.py
this is test.

集合の中のデータ長が長くなると、改行して表示してくれる。

In [17]: data = {i : i + 1000000000 for i in range(7)}

In [18]: data
Out[18]: 
{0: 1000000000,
 1: 1000000001,
 2: 1000000002,
 3: 1000000003,
 4: 1000000004,
 5: 1000000005,
 6: 1000000006}

うれしい補完機能。
f:id:blueskyarea:20180718231354p:plain

感想
補完機能もついていますし、環境があるのであれば通常のインタラクティブシェルの代わりに使ってみたくなるものでした。

ユーザー類似度の計算(協調フィルタリングの前段階) python

今回は Mario さん、Peach さん、Koopa さんに5つのゲームを遊んでもらい、それぞれに5段階点数で評価をつけてもらいました。
※登場人物はフィクションです
その結果が以下です。
data.py

review={
 'Mario': {
 'OCTOPATH TRAVELER': 3.5, 'MARIO TENNIS ACE': 3.0, 'Splatoon2': 2.5, 'Legend of Zelda: Breath of the Wild': 3.5, 'Xenoblade 2': 2.5
  },
 'Peach': {
 'OCTOPATH TRAVELER': 1.5, 'MARIO TENNIS ACE': 3.5, 'Splatoon2': 5.0, 'Legend of Zelda: Breath of the Wild': 5.0, 'Xenoblade 2': 3.0
  },
 'Koopa': {
 'OCTOPATH TRAVELER': 3.0, 'MARIO TENNIS ACE': 3.5, 'Splatoon2': 3.5, 'Legend of Zelda: Breath of the Wild': 4.0, 'Xenoblade 2': 3.5
  }
}

この結果から、これらの3人が”どの位似ている”のかを求めていきます。
いわゆる 協調フィルタリングと呼ばれるものに使われたりするものです。
何らかの評価データから、ユーザー同士の類似度を計算し、似ているユーザーが好むものは、そのユーザーにも好まれやすいという仮説の元、レコメンデーションに使われたりします。

ここでは、ユーザー同士の類似度を以下で計算しました。
user-similarity.py

import sys
from math import sqrt
from data import review

def calc_similarity(member1, member2):
  games = set(review[member1].keys())
  distance_list = []

  for game in games:
    distance = pow(review[member1][game] - review[member2][game], 2)   <- (1)
    distance_list.append(distance)

  print "similarity of " + member1 + " and " + member2
  print 1/(1 + sqrt(sum(distance_list)))  <- (2)

if __name__ == '__main__':
  args = sys.argv
  calc_similarity(args[1], args[2])

1. それぞれの評価(点数)の差を2乗したものを”距離”とし、それぞれの評価が以下に離れているかを数字で表します。
2乗にしているのは、あくまでその距離を絶対値として評価するためですね。

2. それぞれのゲームに対する”距離”の和を求めてから、その平行根を求めています。
※sqrt(sum(distance_list))の部分
この値が大きいほど、”距離が離れている”= "類似していない" ということになります。
そのため、”類似している” 度合いを取るために、その逆数を取る(1から割る)ようにしています。

ちなみに 1 + sqrt(sum(distance_list)) のようにに +1 していますが、もし距離がゼロの場合、sqrt(sum(distance_list)) が 0 になります。
よって、1 / 1 + sqrt(sum(distance_list)) = 1/1 という計算式になり、類似度が"1" という結果になります。
これが完全に一致している状態です。

では、それぞれの類似度をみていきます。

$ python user-similarity.py Mario Peach
similarity of Mario and Peach
0.217129272955

$ python user-similarity.py Mario Koopa
similarity of Mario and Koopa
0.37617851153

$ python user-similarity.py Peach Koopa
similarity of Peach and Koopa
0.294298055086

ということで、Mario さんと Koopa さんが似た趣向を持っていて、Mario さんと Peach さんがあまり似ていないという結果になりました。
確かに、Mario さんと Koopa さんでは、最大で 1.0 しか評価の差はありませんが、Mario さんと Peach さんでは 2.5 の差がある評価もありました。

もし Koopa さんに何かレコメンドしたいのであれば、Mario さんが好んでいるものをレコメンドすれば、刺さりやすいだろうということになります。

何らかのデータを元に仮説を立てるという作業は面白いと思います。
あとは、それを実際に検証が出来ると良いのですが。