TomoProgの技術書

底辺プログラマーが達人プログラマーになるまで

今週は頑張ろうかな

皆さん
こんにちは、こんばんは
TomoProgです。

前回書いたブログが3月12日。
気付けば約1週間ほど放置状態でした。

なぜなら・・・

書くネタが全く思い浮かばなかったから!!

1週間放置してネタが思いついたわけではありませんが、今週はブログ更新できるように頑張ろうと思います。

それではまた。

TomoProg

GitHub
TomoProg (TomoProg) · GitHub

Twitter
TomoProg (@tomoprog_xxx) | Twitter

Vimに自作コマンドを組み込んでみた

皆さん
こんにちは、こんばんは
TomoProgです。

今回は私がPythonのプログラムを書き始めるときに、ほぼ毎回書くコードをVimコマンドで自動入力できるようにしてみました。

それでは頑張っていきましょう。

毎回同じことを書くのは面倒くさい

私がPythonのプログラムを書いているとほぼ毎回書くコードがあります。
それがコチラ

#-*- coding:utf-8 -*-

def main():

if __name__ == "__main__":
    main()

コマンドラインから実行した際に、main関数からプログラムがスタートするように、このコードをほとんどのPythonプログラムで書きます。

1度や2度書くくらいなら何とも思わない量なのですが、Pythonプログラムを作るたびに、毎回毎回書くのは面倒です。

というわけで、この6行をVimのコマンドで入力できるようにしてみます!!

自作コマンドをVimScriptで作成する

Vimで自作コマンドを作る場合はVimScriptというVim専用のスクリプトを使用します。
他のプログラム言語を触ったことがある人は割りと簡単に習得出来そうです。

それでは早速、VimScriptで自作コマンドを作っていきます。

私が作成したコマンドがコチラ

:command! -nargs=1 PyTemplate call PyTemplateFunc(<f-args>)

function PyTemplateFunc(template)
    if a:template == "main"
        :r ~/.vim/mycmd/py_template/main.py
    elseif a:template == "docstr" || a:template == "ds"
        :r ~/.vim/mycmd/py_template/docstr.py
    else
        echo("Nothing Python Template.")
    endif
endfunction

1行目の:commandで始まる行がコマンドを定義している部分です。
今回はPyTemplateという名前でコマンドを定義しました。
コマンドを実行すると、PyTemplateFunc関数が呼び出されるようになっています。

PyTemplateFunc関数の中身を見てみましょう。
templateの中身(PyTemplateコマンドに渡した引数)がmainであれば、main.pyを読み込むようになっています。
ついでにドキュメンテーション文字列のサンプルもコマンドで入力できるようにdocstrの場合はdocstr.pyを読み込みます。

main.pyとdocstr.pyの中身は以下の通りです。

# main.py
#-*- coding:utf-8 -*-

def main():

if __name__ == "__main__":
    main()
# docstr.py
""" This is a documentation string sample

Please Write Desciption

How To Use:
    import sample
    help(sample)
"""

.vimrcにコマンドを登録する

自作コマンドが完成したので、Vimから呼び出せるように.vimrcを編集します。
:sourceの後に作成したVimコマンドファイルのパスを指定します。

:source ~/.vim/mycmd/py_template/py_template.vim

実際にやってみる

登録も出来たので、実際にVimコマンドラインモードで自作コマンドを実行します。

:PyTemplate main

ドキュメンテーション文字列のサンプルを入力する場合は

:PyTemplate docstr
もしくは
:PyTemplate ds

両方実行してみた結果がコチラ

実行結果:
#-*- coding:utf-8 -*-

""" This is a documentation string sample

Please Write Desciption

How To Use:
    import sample
    help(sample)
"""

def main():

if __name__ == "__main__":
    main()

どちらも入力することが出来ました!!

今日の成果

  • Vimの自作コマンドを作成できた!!
  • Pythonプログラムの毎回書く部分を自動化できた!!

今回はVimの自作コマンドを作って、毎回の作業を効率よくしてみました。
これから少しだけPythonでのプログラム作成が快適になりそうです。

それではまた。

TomoProg

GitHub
TomoProg (TomoProg) · GitHub

Twitter
TomoProg (@tomoprog_xxx) | Twitter

応用情報技術者試験まで残り38日となりました

皆さん
こんにちは、こんばんは
TomoProgです。

一ヶ月くらい前にこんな記事を書きました。
tomoprog.hatenablog.com

そうです。
実は応用情報技術者試験に申し込んでいたんです。

タイトル通り応用情報技術者試験まで残り38日・・・。



全く勉強していない!!



というわけで、残り38日間勉強します。

それではまた〜

TomoProg

GitHub
TomoProg (TomoProg) · GitHub

Twitter
TomoProg (@tomoprog_xxx) | Twitter

サイコロの目が6分の1の確率って本当!?

皆さん
こんにちは、こんばんは
TomoProgです。

なんか昨日一日で約50PVぐらいされたようで、今月も100PVを達成出来ました!!
多くの人に見ていただけるのは非常にありがたいことです。
これからもマイペースに記事を書いていけたらと思います。

今回はサイコロの確率をPythonで計算してみました。

それでは頑張っていきましょう。

6回振れば1回は出るって本当?

ふと思ったんです。
サイコロを振って指定した目が出る確率は6分の1です。
でも、実際に6回振っても出ないときもある・・・

じゃあだいたい何回振れば6分の1になるんだろう?

今回はこんな疑問をPythonで解いていこうと思います。

サイコロプログラムを書く

まずはサイコロをプログラムで書いてみます。

#-*- coding:utf-8 -*-
import sys
import random

def dice_roll():
    """
        Content:
            サイコロを振る
    """
    return random.randint(0, 5)

def main():
    """
        Content:
            エントリーポイント
    """
    #--------------------------------
    # サイコロを振る回数を取得
    #--------------------------------
    roll_num = int(sys.argv[1])

    #--------------------------------
    # サイコロを振る
    #--------------------------------
    dice_list = [0]*6
    for i in range(roll_num):
        dice_list[dice_roll()] += 1

    #--------------------------------
    # 結果表示
    #--------------------------------
    print(dice_list)

if __name__ == "__main__":
    main()

実行引数にサイコロを振る回数を指定します。
指定した回数分だけdice_roll関数を呼び出し、出た目の回数をカウントしていきます。

実際に100回サイコロを振ってみた結果は以下のようになりました。

実行:
$ python3 dice.py 100
実行結果:
[21, 11, 13, 11, 21, 23]

6分の1で出るのであれば

100 ÷ 6 = 16.66666....

それぞれの出た回数が16付近であればいいのですが、そうでもなさそうですね。

実行結果をグラフ化する

上記のプログラムでサイコロを振るプログラムは完成しました。
もっと沢山サイコロを降る前に、もう少し実行結果を分かりやすくするためにグラフで表示してみようと思います。

Pythonにはもちろんグラフを表示するための便利なモジュールも用意されています。
今回はmatplotlibというモジュールを使用します。

ただ、このmatplotlibは他のモジュールとの依存関係が強いのかインストールするのがかなり大変でした。
Ubuntu14.04で使用する場合は以下のコマンドを順に入力していけばインストール出来ました。

$ sudo apt-get install python3-pip
$ sudo apt-get install libfreetype6-dev
$ sudo apt-get install libxft-dev
$ sudo apt-get install libffi-dev
$ sudo pip3 install cairocffi
$ sudo pip3 install matplotlib

さて、matplotlibを使用する環境が整ったので、先ほどのサイコロプログラムを改良し、円グラフを表示してみます。

#-*- coding:utf-8 -*-
import sys
import random
import matplotlib.pyplot as plt

def dice_roll():
    """
        Content:
            サイコロを振る
    """
    return random.randint(0, 5)

def main():
    """
        Content:
            エントリーポイント
    """
    #--------------------------------
    # サイコロを振る回数を取得
    #--------------------------------
    roll_num = int(sys.argv[1])

    #--------------------------------
    # サイコロを振る
    #--------------------------------
    dice_list = [0]*6
    for i in range(roll_num):
        dice_list[dice_roll()] += 1

    ##--------------------------------
    ## グラフ表示
    ##--------------------------------
    fig = plt.figure(figsize=(8, 8))
    ax = fig.add_subplot(111)
    labels = [str(x) for x in range(1, 7)]
    ax.pie(dice_list, labels=labels, autopct="%1.1f%%", startangle=90, shadow=True)
    plt.show()

if __name__ == "__main__":
    main()

matplotlibを使う場合はimport文にmatplotlibを書いておきます。
import文でasを使用するとimportしたモジュールに別名を付けられるので便利です。
ax.pieでdice_listの中身の内容で円グラフを書くように指定しています。

実際に実行して表示してみます。

実行:
python3 dice.py 100

f:id:TomoProg:20160307234457j:plain

リストの結果を表示するよりもグラフで表示できたほうが格段に見やすくなりました。

沢山振ってみる

グラフの表示も可能になったので、今度は沢山のパターンでサイコロを振ってみます。
30回ずつサイコロを振る回数を増やしていくとこんな感じです。
(スタートが左上でZ字の順です。)
f:id:TomoProg:20160308000227j:plain
f:id:TomoProg:20160308001739j:plain

1枚目の上段のグラフ(30回〜120回)はバラバラなイメージが有りますが、下段(150回〜240回)は結構綺麗な円になりました。
2枚目(270回〜480回)に入るとかなり6等分に近づき、あまり変化が見られなくなりました。

まとめ

  • 大体150回くらいから6分の1の確率に近づいてくる。
  • 大体300回くらいサイコロを振れば6分の1の確率にかなり近くなる。

今回はPythonでサイコロを振ってグラフを描いてみました。
やるたびにまた結果は違うと思うと確率って奥深いですね。

それではまた。

TomoProg

GitHub
TomoProg (TomoProg) · GitHub

Twitter
TomoProg (@tomoprog_xxx) | Twitter

Pythonで百人一首ボットを作ってみる!! 最終回

皆さん
こんにちは、こんばんは
TomoProgです。

前回のボット作ってみるからまた日が空いてしまいましたが、
Python百人一首ボットを作ってみるシリーズもこれで最後です。

パート2までで百人一首の画像と歌を投稿する機能を実装しました。
今回は投稿した百人一首の現代語訳を投稿する機能を実装してみます。

パート1、パート2を見ていない方はそちらからどうぞ!!
tomoprog.hatenablog.com
tomoprog.hatenablog.com

それでは最終回頑張っていきます!!

現代語訳データを収集する

現代語訳は以下のサイトの現代語訳を拝借しました。
www.hyakunin.stardust31.com

収集する方法はパート2でもやりましたが、

  1. 上記のサイトを全コピーする
  2. テキストエディタで不要な行を削除する
  3. 表計算ソフトに貼り付ける
  4. CSVファイルとして保存する

この方法でいきます!!

表計算ソフトに貼り付けるとこんな感じです。
f:id:TomoProg:20160303213026j:plain

左から

  • 歌人
  • 歌人のふりがな
  • 上の句
  • 下の句
  • 現代語訳

の順です。

これで、投稿する現代語訳の収集は完了しました!!

現代語訳を投稿するコードを実装する

さて、現代語訳の収集が終わったので、現代語訳を投稿するコードを追記します。

def karuta_tweet(karuta_info):
    """
        Content:
            つぶやく
    """

    global app
    global app_up

    #------------------------------
    # かるた画像つぶやき
    #------------------------------
    # 画像ファイル読み込み
    try:
        file_path = os.path.join("./karuta_img/", karuta_info["img"])
        with open(file_path, "rb") as image_f:
            image_data = image_f.read()
    except Exception as e:
        logger.write("画像ファイル[{filename}]の読み込みに失敗しました".format(filename=filepath))
        logger.write(str(e))
        return False

    # つぶやきメッセージ作成
    tweet_msg = (
        karuta_info["first_part"] + "\n" +
        karuta_info["last_part"] + "\n" +
        karuta_info["name"])

    # つぶやく
    try:
        id_img = app_up.media.upload(media=image_data)["media_id_string"]
        app.statuses.update(status=tweet_msg, media_ids=id_img)
    except Exception as e:
        logger.write("画像のTweetに失敗しました。")
        logger.write(str(e))
        return False

    time.sleep(30)

    #------------------------------
    # 現代語訳つぶやき
    #------------------------------
    tweet_msg = karuta_info["translate"]
    try:
        app.statuses.update(status=tweet_msg)
    except Exception as e:
        logger.write("現代語訳のTweetに失敗しました。")
        logger.write(str(e))
        return False

    return True

百人一首ボットプログラムのTwitterに投稿する関数を抜き取りました。
歌、歌人、かるた画像の投稿をしたあとに、time.sleep(30)で30秒スリープ後、日本語訳を投稿するようになっています。

少し雑ですが、例外処理を入れ、何かしらの例外があった際には自作のロガープログラムでログをファイルに出力するようにしています。

ボットを起動しておくサーバを借りる

今までは自分が使っているPCの仮想環境から投稿していました。
しかし、一日中投稿しようとすると、PCをずっと起動していないといけないのです。

スリープも出来ないのはきついと思ったので、さくらインターネットVPSを借りました。
vps.sakura.ad.jp

ちなみに月額972円のHDD100Gのプランです。
今回はそこから起動してみます。

VPSサーバの設定

さくらのVPSサーバは標準だとOSがCentOS6ですが、CentOSをほぼ触ったことがないので、Ubuntu Server 14.04を使用することにしました。

とりあえず、Pythonを使用できる環境を整えます。
Ubuntu14.04では標準でPython3.4.3がインストールされているので、Python自体のインストールは不要です。

Pythonのパッケージ管理システムであるpipを使えるように以下のコマンドを入力します。

$ sudo apt-get install pip3

pip3ではなくpipでインストールすると、Python2系のpipがインストールされるので、注意が必要です。

pipがインストール出来たら、Pythonの仮想環境を構築するためにvirtualenvをインストールします。

$ sudo pip3 install virtualenv

virtualenvはPythonの仮想環境を作成する便利なモジュールです。
virtualenvを使えば作成した仮想環境ごとにPythonのバージョンやインストールするモジュールを分けることができます。

さて、あとは仮想環境にtwitterモジュールをインストールします。

# 仮想環境にて
$ pip install twitter

これでサーバの環境構築は終了です。

サーバから起動する

さて、環境が整ったので、サーバから起動してみます。
仮想環境にて以下のコマンドを入力します。

# 仮想環境にて
$ python karuta_bot.py &

この"&"記号をつけることで、バックグラウンドでプログラムを起動出来ます。

起動して約一日ほど放置してみた結果がこんな感じです。
twitter.com

画像につづいて現代語訳を投稿することが出来ました!!

今日の成果

  • 現代語訳を投稿することが出来た!!
  • サーバから起動することで、一日中起動しておくことに成功した!!
  • 百人一首ボットが完成!!

やっと百人一首ボットが完成しました!!
Twitterボットは簡単に作成するサービスが既にありますが、自分で1から作ってみるというのが新鮮で面白かったです。
この記事を見て作りたくなったと思った人は是非自分で作ってみてください!!

ここまで読んでいただきありがとうございました。
それではまた。

TomoProg

GitHub
TomoProg (TomoProg) · GitHub

Twitter
TomoProg (@tomoprog_xxx) | Twitter

vsftpdの設定で謎のエラーにハマった

皆さん
こんにちは、こんばんは
TomoProgです。

今日はTwitterボット用にサーバを設定していたら、vsftpdの設定でハマったのでここに書いておきます。
ちなみにサーバは「Ubuntu14.04 amd-64 LTS」です。

それでは頑張っていきましょう。

vsftpdをインストールする

まずはvsftpdをインストールします。
インストールするには以下のコマンドをターミナルで入力します。

$ sudo apt-get install vsftpd

インストールはこれだけです。

vsftpd.confを設定する

vsftpdのインストールが完了したら、vsftpdの設定ファイルであるvsftpd.confを設定します。
ちなみにvsftpd.confの場所は/etc/vsftpd.confです。

# 書き込み許可(コメント解除)
write_enable=YES

# アスキーモードの転送許可(コメント解除)
ascii_upload_enable=YES
ascii_download_enable=YES

# ログイン時のバナーメッセージ(コメント解除)
# (ログイン時にバージョン情報の表示を行わないように設定)
ftpd_banner=Welcome to blah FTP service

# chroot許可(コメント解除)
chroot_local_user=YES
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd.chroot_list

# ディレクトリごと一括での転送有効(コメント解除)
ls_recurse_enable=YES

# FTP接続時のルートディレクトリを設定(追記)
local_root=/home/tomoprog/ftp_root

# これはよく分からんがとりあえず追記
seccomp_sandbox=NO

ちなみに設定は下記のサイトを参考にさせていただきました。
http://www.server-world.info/query?os=Ubuntu_14.04&p=ftpwww.server-world.info

さて、chroot_list_fileを有効にしたので、指定したリストファイルを作成しておきます。
今回はすべてのユーザのFTPでのルートディレクトリをlocal_rootに指定したディレクトリにしたかったので、空のリストファイルを作成します。

$ sudo touch /etc/vsftpd.chroot_list

すべての設定が終わったので、vsftpdを再起動します。

$ sudo service vsftpd restart

500 OOPSでハマる

さて、設定が終わったので、早速別の端末からFTP接続してみます。

# xxx.xxx.xxx.xxxはFTPサーバのIPアドレスを指定
$ftp xxx.xxx.xxx.xxx

上記のコマンドを入力すると、ユーザとパスワードを求められるので入力します。
すると以下のエラーメッセージが返ってきました。

500 OOPS: vsftpd: refusing to run with writable root inside chroot()

英語とか分からんよ・・・。
というわけでGoogle先生に聞いてみると以下のサイトを教えてくれました。
ftp接続でrefusing to run with writable root inside chrootが出た時の対処法 – ムッシューのIT備忘録

なるほど。
chrootした先に書き込み権限があるとエラーとなるようです。
かと言って書き込み権限を消してしまうとアップロードできないので、vsftpd.confに以下の一行を追記します。

allow_writeable_chroot=YES

これでもう一度FTP接続してみると・・・

230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.

接続に成功しました!!

まとめ

  • refusing to run with writable root inside chroot()の対処方法はallow_writeable_chroot=YESをvsftpd.confに追記する


今日はvsftpdを設定してみました。
エラーが出てから、たった1行の追加に2時間かかったのは内緒です。
それではまた。

TomoProg

GitHub
TomoProg (TomoProg) · GitHub

Twitter
TomoProg (@tomoprog_xxx) | Twitter

Pythonで閏年を判定してみた

皆さん
こんにちは、こんばんは
TomoProgです。

昨日、2月29日だった・・・
今年は閏年だ!!
というわけで、記念すべき20記事目はPythonを使って閏年を判定してみます。

それでは頑張っていきましょう。

閏年の判定方法

閏年というと4年に一度あるんだから4で割り切れる年が閏年だ!!
と考えてしまう人も多いと思うのですが、実は意外と複雑なんです。

  • 西暦が4で割り切れる年はうるう年
  • ただし、4で割り切れても100で割り切れる年はうるう年でない
  • ただし、100で割り切れても400で割り切れる年はうるう年

それでは、上記を踏まえてPython閏年判定プログラムを書いてみたいと思います。

閏年判定プログラムを書いてみる

さっそく閏年判定プログラムを書いてみました。

#-*- coding:utf-8 -*-

def check_leap_year(year):
    """ 閏年判定 """
    if year % 400 == 0:
        return True
    elif year % 4 == 0 and year % 100 == 0:
        return False
    elif year % 4 == 0:
        return True
    else:
        return False

def main():
    """ エントリーポイント """
    years = [2016, 2000, 1990, 1980]

    for year in years:
        if check_leap_year(year):
            print(str(year) + "年は閏年です。")
        else:
            print(str(year) + "年は平年です。")

if __name__ == "__main__":
    main()
実行結果:
2016年は閏年です。
2000年は閏年です。
1990年は平年です。
1980年は閏年です。

check_leap_year関数で閏年かどうかを判定し、閏年か平年かを出力するプログラムです。
実行結果を見る限り、閏年を正確に判定できているみたいです。

calendarモジュールで閏年を判定する

上記のように自分で閏年判定を実装してもよいですが、Pythonにはもっと簡単に閏年を判定する方法があります。

その方法がcalendarという標準モジュールを使う方法です。

実はcalendarモジュールには年を渡すと閏年かどうかを判定してくれるisleapという関数がすでに実装されています。

isleap関数を使った場合のプログラムを書いてみました。

#-*- coding:utf-8 -*-

import calendar

def check_leap_year(year):
    """ 閏年判定 """
    if calendar.isleap(year):
        print(str(year) + "年は閏年です。")
    else:
        print(str(year) + "年は平年です。")

def main():
    """ エントリーポイント """
    check_leap_year(2016)
    check_leap_year(2000)
    check_leap_year(1990)
    check_leap_year(1980)

if __name__ == "__main__":
    main()
実行結果:
2016年は閏年です。
2000年は閏年です。
1990年は平年です。
1980年は閏年です。

上記のプログラムと同じ結果が得られました!!
calendarモジュールを使った方が、短く簡単に書くことが出来ます。

まとめ

  • Python閏年を判定する際はcalendarモジュールのisleap関数が使える

今回はPython閏年の判定をしてみました。
次は30記事目指して頑張ります!!
それではまた。

TomoProg

GitHub
TomoProg (TomoProg) · GitHub

Twitter
TomoProg (@tomoprog_xxx) | Twitter