PHPUnitテストを初めてやってみる

開発におけるテストの自動化ツールとしてPHPUnitを使ってみます。
PHPUnitのインストールや環境などは既に揃っているものだとします。

テストの目的
・想定通りの操作をするか
・デグレしないか

まあいろいろあると思いますがテストなんてしないよりした方がいいですよね。
作るのは面倒かもしれませんが自動化できるところは自動化しておけば人がテストするよりも楽で結果的に速くなるという考え方もうなずけます。

注意点としては初めは難しいと思うのでちょっとずつ書くこと。
完璧を意識しすぎないこと(挫折する可能性大)

早速簡単なテストコードを書いてみたいと思います。
今回は半角英数のバリデーションが意図通りの操作をしてくれるかテストをしたいと思います

テストする関数

function isAlphanumeric($string){
    return preg_match("/^[a-zA-Z0-9]+$/", $string);
}

PHPUnit_Framework_TestCaseを継承したクラスがテストクラスになります。
テストするメソッドは「test」から始めるようにします。

OKなケースとNGなケースを書きます。

class hogeTest extends PHPUnit_Framework_TestCase {

    public function testIsAlphanumeric() {
    // OK
    $strings = array("aaa", "aB12", "fd9fasyf7","fawiefawb23r9y");
    foreach ($strings as $string) {
        $this->assertEquals(1, isAlphanumeric($string));
    }

    // NG
    $strings = array(null, "", "あああ", "カキク", "…", "@", "s", ":@[]\/.", "金", "AAあ");
    foreach($strings as $string) {
        $this->assertEquals(0, isAlphanumeric($string));
    }
}

assertEquals(x, y)
→第一引数(x)と第二引数(y)が等しいかどうかを調べてくれるメソッドです。

さてテストが書けたので実行します。

phpunit hogeTest.php

結果は以下の通り。

1つのテストを実行し計14個の検証が行われOKとでました。めでたしです。

Python 再帰関数 初めの一歩

再帰関数 超むずい。

ファミマの焼き鳥 超うまい。(食べたことないけど)

というわけで、再帰関数について勉強中であります。

 

今まで、縁がなかったんですよ。作る必要なかったっていうか。

しかし、ディープラーニング系だと結構出てきますよね!

こちらのサイト拝見して勉強させて頂きました。m(_ _)m

http://www.geocities.jp/m_hiroi/light/pyalgo01.html

とりあえず、ここに載っているフィボナッチ関数の簡単なバージョンを作ってみます。

def fibo(n):

    print('nだよ %s' %n)
    if n == 0 or n == 1: return 1

    return fibo(n - 1) + n

v=fibo(5)
print('vだよ %s' %v)

結果は次の通り。

nだよ 5
nだよ 4
nだよ 3
nだよ 2
nだよ 1
vだよ 15

再帰関数初心者としては、return が最後しか返ってこないのが不思議。

弊社の俊英 Nくんが教えてくれたのですが、
「まずは再帰の中をぐるぐるやって、終わった時にreturnします。」
だそうです。

もうちょっと飛躍させて、次のようなのを作ります。

print('はじまり')
array = [1,2,3]

def search(s):

    print("sだよ %s" %s)

    if s not in array:

        v = s + 10
        print("途中で帰るv %s" % v)
        return -v

    next_s = s + 1

    v = search(next_s)

    print('最後まで行ったv:%s s:%s' %(v,s))

    return -v

for x in range(5):
    value = search(x)
    print("vだよ %s" %value)

print('おわり')

出力は次の通り。

はじまり

sだよ 0
途中で帰るv 10
vだよ -10
sだよ 1
sだよ 2
sだよ 3
sだよ 4
途中で帰るv 14
最後まで行ったv:-14 s:3
最後まで行ったv:14 s:2
最後まで行ったv:-14 s:1
vだよ 14
sだよ 2
sだよ 3
sだよ 4
途中で帰るv 14
最後まで行ったv:-14 s:3
最後まで行ったv:14 s:2
vだよ -14
sだよ 3
sだよ 4
途中で帰るv 14
最後まで行ったv:-14 s:3
vだよ 14
sだよ 4
途中で帰るv 14
vだよ -14

おわり

最後まで行ったv というのが結局returnされないのは、さっきのくだりで学習したのですが、初心者にとって解せぬポイントは

最後まで行ったv:-14 s:3
最後まで行ったv:14 s:2
最後まで行ったv:-14 s:1

なんですよ。

なんで、これ、sの順番が逆なの??
sは 1,2,3 の順じゃないの??

これも、弊社の俊英かつサイキッカーのN君が教えてくれました。

「再帰関数は、枝分かれしていって評価するんですが、最後の枝までいったら逆順に評価していくんです。」

エッッ そうなんだ。( ゚Д゚) むずかしー!!

サイキッカーへの道は遠いようです。

Pycharm デバッグできなくなる

私はPythonの開発をPycharmというIDEで行っていますが、まだあんまり全貌がつかめていません。Pycharmは2017.3 Communitu Editionです。

特に、実行環境を適当にやっていると、おかしくなります。(適当だから仕方ないね…。)

今日はデバッグできなくなるという事態に遭遇しました。

そんな時は、ここを確認してみてください。

File→Settingus→Project Interpreter

でPythonの実行環境が選べますが、ほかのプロジェクトの実行環境を選んでないでしょうか?

そうすると、実行はできるけれどもデバッグできないようです。

では、今やっているプロジェクトの実行環境を作る方法です。

①File→Settingus→Project Interpreter って選択した後、下記の画面の赤丸の部分をクリックします。

②Add Localを選択します。

③New environmentを選択します。

Base InterpreterでPythonの実行環境を選んでください。

すると、上のLocationのところに、今選択しているプロジェクトの名前+venv という形でパスが自動的に入ると思います。

④OKをクリック

 

Python ArgumentParserでコマンドラインから実行時に強制的に引数を渡させる

きっと書いておかないと忘れるので書いておきます。

ArgumentParserという便利なライブラリがあります。

公式サイトはこちら。

平たく言うと、コマンドラインから

python main.py

って走らせるときに、引数がないと動作しないようにする、とか、ヘルプを表示させたりとか、コマンドラインの引数に応じて、実行することを変えたりすることができるのです。

超シンプルな使い方は下記のようにします。

import argparse

parser = argparse.ArgumentParser(description='Process some integers.')

parser.add_argument('integers', metavar='N', type=int)

args = parser.parse_args()
print(args.integers)

コマンドラインから引数なしで実行すると、次のように表示されます。

 

G:\test>python argparse_sample.py
usage: argparse_sample.py [-h] N
argparse_sample.py: error: the following arguments are required: N

引数を渡すと、次のように引数が表示されます。

G:\test>python argparse_sample.py 5
5

Open AI Gym 初めの一歩

強化学習というのは、機械学習の一つの分野ですが、学習がうまくいっているのかどうか調べるのは難しい課題なんですよね。

そこで、Open AIのGymというツールがあります。

https://gym.openai.com/

なんか、ちょいちょい見る、トンカチみたいなのがゆらゆら揺れてる動画…。これだったんだw

やり方は、次のサイトにある通りですが

https://gym.openai.com/docs/

英語を読むのも「ええい!面倒!!」ということもあると思いますので、超簡単に記載しておきます。

pip install gym

でインスコします。

import gym
env = gym.make('CartPole-v0')
env.reset()
for _ in range(1000):
    env.render()
    env.step(env.action_space.sample()) # take a random action

これで走らせてみると、トンカチが一回転して、画面の外に消えちゃいますねw

他に、山を登る車、

'MountainCar-v0'

ってのもあります。さっきのgym.makeの中身を変えるだけです。

import gym
env = gym.make('MountainCar-v0')
env.reset()
for _ in range(1000):
    env.render()
    env.step(env.action_space.sample()) # take a random action

これも実行してみると、右往左往してまったく山を登りそうにない車が表示されます。
やはり、ランダムに何かを行ってみても何も結果は得られない… という人生の教訓を得た気になりますね!!

ちょっと進めて下記のようにします。

import gym
env = gym.make('CartPole-v0')
for i_episode in range(20):
    observation = env.reset()
    for t in range(100):
        env.render()
        print(observation)
        action = env.action_space.sample()
        observation, reward, done, info = env.step(action)
        if done:
            print("Episode finished after {} timesteps".format(t+1))
            break

実行すると、ポールが倒れたり、画面の外に行ったりしません。

一瞬おお!と思いますが、落ち着こう。

 

observationというのをprintするようにしたので、次のようにprintされますが

[-0.06550298 -0.62622142 0.12089567 0.99951833]
[-0.07802741 -0.82273366 0.14088604 1.32759199]
[-0.09448208 -0.62964272 0.16743788 1.08210892]
[-0.10707494 -0.4370784 0.18908006 0.846299 ]
[-0.1158165 -0.24496922 0.20600604 0.6185299 ]
….
Episode finished after 16 timesteps

そもそも、この4つの数字なんだ!って話ですよね。

この答え、CartPoleのソースコード見ると書いてありました。(他のところにもかいてほしぃ…)
https://github.com/openai/gym/blob/master/gym/envs/classic_control/cartpole.py

Observation:
Type: Box(4)
Num Observation Min Max
0 Cart Position -4.8 4.8
1 Cart Velocity -Inf Inf
2 Pole Angle -24° 24°
3 Pole Velocity At Tip -Inf Inf

そして、そもそも終了条件ってなんなん??

って思うと、これもソースコード見ると書いてありました。

Episode Termination:
Pole Angle is more than ±12°
Cart Position is more than ±2.4 (center of the cart reaches the edge of the display)
Episode length is greater than 200
Solved Requirements
Considered solved when the average reward is greater than or equal to 195.0 over 100 consecutive trials.

1.ポールの角度(さっきのObservationの3つ目ですね)が±12を超える
2.カートのポジション(さっきのObservationの1つ目ですね)が±2.4を超える
3.エピソードの長さが200以上
4.解決されたと思われる 平均的な報酬が100回の連続したトライアルの中で195を超える

報酬は、1回進めて、倒れなかったら1もらえるので、200回エピソードを繰り返して、195回目まで倒れなければ、そのモデルが優秀ということで、もうOKってことなんですかね。

Actionは左に行くか、右に行くかだけですが、これにVelocityなどが加わって、どのぐらいのポジションを進むかが決定されるようです。

倒れなくなったように見えますが、そうではなく、倒れようとしたところで(doneがTrueになる)env.resetされるので、倒れなくなったように見えるだけです。