Tips

IT技術系Tips

Stringクラス内のfor文の実装について(その1)

先日客先に出向いた時、あまりに待ち時間が長くて暇だったのでStringクラスの実装を見ていた。
そこで気になる実装があった。

for (int i=; i++ < value.length;) {
   ...省略

iをインクリメントしている場所がおかしい。

普通ならこう書く

for (int i=; i < value.length; i++) {
   ...省略

もしかしてi++ < value.length;って書いたほうが早いのか?
Stringクラスはfinalで定義されているのは、速度面でほんの少しだけ優位に立てるからだ。
(java8でもそうなのかは知らんけど)
このforの書き方も速度面を気にしてのことかもしれない。

この書き方の違いで速度に違いが出たとしても、本当にわずかな差なんだろうけど、
自分の仕事上、このわずかな差が効いてくるところがあるので気になる。

まだ動作については調べてないわけだが、今後どう調べて行くかというと・・・


まずは中間コードに差がでるかどうか調べる。
調査にはjavapコマンドを使う。
javap - Java クラスファイル逆アセンブラ

これで差があるようなら、次は実計測を行う。
この計測がかなり面倒なわけだが、適当に計測用のコードを書くと、コンパイラが最適化しちゃって処理そのものを捨てちゃったりするので
気をつけて検証用のソースを書かなきゃならない。


続きは来週あたり?

sqliteのDBを比較するスクリプトを書いた

諸事情でgithubからは消してたけど復活させた。
DBを使うテストなんかで呼び出す。

結果を標準出力にテーブル単位で出力して、差があるようならdiff.dbにレコード単位で保存。
実行すると、データに差がなければOK、差があればNGって出る。

+--------------------------------+--------------------------------+--------+
| old_tbl                        | new_tbl                        | status |
+--------------------------------+--------------------------------+--------+
| t060_filtering_collate_history | t060_filtering_collate_history |   OK   |
| t022_text                      | t022_text                      |   OK   |
| t027_alias_num                 | t027_alias_num                 |  *NG*  |
| t013_num                       | t013_num                       |   OK   |
+--------------------------------+--------------------------------+--------+

インストール

ライブラリを入れる。

pip install prettytable

githubからソースを持ってくる。
github.com

sqliteも入れてパスを通すかsqlite_diff.pyと同じディレクトリに置く。
windowsならsqlite-tools-win32-x86-xxxxxxxxx.zipってやつ。
SQLite Download Page

使い方

a.dbとb.dbを比較して、どのテーブルのどのデータに差分があるのか調べる。

python sqlite_diff.py A.db B.db


列を無視して比較。
全てのテーブル共通で更新日がある場合なんかに使う。

python sqlite_diff.py A.db B.db --ignore modified


テーブルごとに無視する列を決める。
テーブル名:列名のフォーマット。

python sqlite_diff.py A.db B.db --ignore-tbl-col test:id


テスト対象外としたいテーブルがある場合。

python sqlite_diff.py A.db B.db --ignore-tbl test


PKを指定する。
テーブル内のデータ同士はPKを使って比較するので、PK制約はサロゲートキーであるidに貼ってあるけど、ほんとはparent_table_id,cif_person_id,black_pkで一意になるってことならこうする。

python sqlite_diff.py A.db B.db --ignore id collate_date --unique t063_filtering_collate_result:parent_table_id,cif_person_id,black_pk

余談

sqliteダウンロードしたら、「sqldiff.exe」ってのが付いてたんだけど、これってもしかして
ここで紹介してるsqlite_diff.pyとかぶってる?

分足の為替データを取得するスクリプト(Python)

ロシア語のサイトだが、アカウント作成とか面倒なことなしにデータが置いてあった。
言語の選択で英語も可。
Архив котировок Forex для ProSuite и MetaStock


いちいち手動でダウンロードして全部解凍するのがめんどいから
各ページにアクセスしてzipをダウンロードし、解凍して保存するスクリプトを書いた。
github.com

こないだ試したプログレスバー(tqdm)も使った。
kaerouka.hatenablog.com

前回はipythonから試してダメだったが、cPythonではさほど問題なかった。
少しだけ問題があって、たぶんtqdmは別スレッドで動作しているので、tqdmでプログレスバーを出す前に
標準出力に出力してるとちょっと変な動作になった。
こんな感じ。

データの配置場所のURLを収集中。
  0%|          | 0/4698 [00:00<?, ?it/s]収集完了。
データを取得中。
 31%|███       | 1464/4698 [52:19<1:56:39,  2.16s/it]

収集完了のprintは、プログレスバーを出すより前にやってるはずなんだけど、こんなんなっちゃった。
対処法がありそうだけど面倒だから放置。


プログラムの実行速度だけど、終わるまでに3時間くらいかかる。
5000個近いファイルのダウンロードになるみたいで、並列処理無しじゃこんなもんか。


2016/02/03 追記
tqdm使う前にflushしたらずれなくなった。

    print("データを取得中。")

    # tqdmが非同期で、表示がずれるためいったんflush
    stdout.flush()

    for data_url in tqdm.tqdm(urls):

並列処理しながらtqdm使ったらどうなっちゃうんだろう・・・w

数学わかんねーからプログラムでズルしてみた 1次方程式編(Python:Sympy)

最近Smiteってゲームにハマってて、これの勝率が気になった。
基本5:5で戦うゲームなんだけど、マッチングシステムがよく出来てて、長いことやってるとだいたい勝率が50%くらいになるようになってる。

現在の戦績が1190戦610勝580負。
勝率51%くらい。
さぁ、何連勝したら勝率52%になるんでしょうか!

今後の戦闘回数 = 今後の勝利回数 = xとすると、こんな感じ

(610 + x) / (1190 + x) = 0.52

sympyで書くとこう

In [1]: solve(Eq((610 + x) / (1190 + x), 0.52))
Out[1]: [18.3333333333333]

方程式は解が複数あったりもするから配列で答えが返ってくるんだね。
戦う回数に小数とか無いから切り上げて

In [2]: solve(Eq((610 + x) / (1190 + x), 0.52))[0].ceiling()
Out[2]: 19

はい、19連勝すれば勝率52%を超えます!

勝率51%の自分がストレートに19連勝できる確率は

0.51 ^ 19

これをpython(sympy)で書くと

In [3]: 0.51**19
Out[3]: 2.778646798186872e-06

指数とかよくわかんない。
新人の頃、計算結果を指数表記で出してお客さんに「こんなんわけわからんわ!」と、怒られたことがある。
わざとやったわけじゃないんだけどね、PostgreSQLの列定義をfloatにしててそのまま出力してたら出ちゃってね・・・。
なんて苦い経験があるから指数表記をやめる。

※変数aはpythonのbuiltinのfloatなんで、sympyのFloatにしてevalf使って桁数指定した。

In [4]: a=0.51**19

In [5]: Float(a).evalf(50)
Out[5]: 0.0000027786467981868720860121231563955035426261019892991

はい、まだよくわかんない。

分数にしよう。

In [6]: Rational(a)
Out[6]:
   3280447126872685
──────────────────────
1180591620717411303424


わかんねぇよ!!
つーかほんとかよこれ。
ドキュメントを見てみると
SymPy Core — SymPy 0.7.6.1 documentation

If the simpler representation of the float is desired then consider limiting the denominator to the desired value or convert the float to a string (which is roughly equivalent to limiting the denominator to 10**12):

ふむ、文字列にするか分母を制限しろとか書いてある。

In [7]: Rational(str(b))
Out[7]:
   277864679818687
─────────────────────
100000000000000000000

そろそろ自分が何をしたいのかわかんなくなってきた。

あー、そうだ。
勝率51%の自分がストレートに19連勝できる確率は「1/1000 くらい!」とか適当な感じで表して嘆きたかったんだ。

もういいよ。

In [3]: 0.51**19
Out[3]: 2.778646798186872e-06

なんだから

3/100万くらいっしょ?
それとも分母を制限してこんな感じのがまし?

In [8]: ra = Rational(str(b))
In [9]: ra.limit_denominator(1/a)
Out[9]: 1/359887

最初からわかってたけど、sympy使いこなしてないならwindows付属の関数電卓と紙でやったほうが断然早いよね。
でもlimit_denominatorはちょっと好き。
この関数見つけたのは嬉しい。

簡単にプログレスバーを表示できるtqdmを使ってみた(Python)

この記事の最初で紹介されてたから使ってみた。
postd.cc

こんなふうに書くと

for item in tqdm(items):
    process(item)

こんな感じで進捗を表示してくれるらしい。

18%|█████████                               | 9/50 [00:09<;00:41,  1.00it/s

リアルタイムで進捗を見たいなら、やっぱipythonとか使うよね。
やってみた。

In [5]: for i in tqdm.tqdm(range(10)):
   ...:     sleep(.1)
   ...:
 10%|笆遺毎笆遺毎笆遺毎笆遺毎笆・                                                                         | 1/10 [00:00<
 20%|笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆・
 30%|笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆・
 40%|笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺柾
 50%|笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎
 60%|笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎
 70%|笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎
 80%|笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎
笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆・               | 8/10 [00:00<00:
 90%|笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎
笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆・       |
100%|笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎
笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎

In [6]:

\(^o^)/

相変わらずwindowsでipython使ってなんかやると文字化けで面倒なことになる。
前にコマンドプロンプトのコードページ変えたり、pythonの標準出力の文字コード変えたりとか試したけど結局ダメだった記憶が・・・。
たぶん今回の問題も無理なんだろう。

2016/02/02 追記
cPythonならそこそこうまくいった。
kaerouka.hatenablog.com

正規表現

AAAAが含まれない行全体

^(?!.*AAAA).+$

AAAA、BBBBのどちらも含まれず、yyyy-mm-ddで始まる行全体

ログ検索に使用したのでメモ

^\d{4}-\d{2}-\d{2}(?!.*(AAAA|BBBB)).+$

tsvの8項目目が不正な生年月日の場合、空文字に書き換え

置換前:^(([^\t]*\t){7})(?!((1|2)[0-9]{7}\t)|\t)[^\t]+(.+)$
置換後:$1$+

サクラエディタ/bregonig.dll v3.0.2
この正規表現は、システム導入当日に「生年月日は8桁固定で数値のみって話だったじゃん!」
みたいな状況になったら即席で調べるために使用する。

不正な生年月日に関しては厳密な判断でなく、先頭が1または2で、固定8桁を正常扱いと判断している。
$+はサクラエディタ/bregonig.dll固有の機能?で、最後に一致した部分文字列を意味する。
ようは最後の(.+)を使える。

やっぱ客先サーバーがWindowsなら秀丸は有償で厳しいからサクラだよね~。
サクラは数ギガのテキストファイルが開けなかったりするから、その点はcygwinsedとかで補完が必要かもしれないけど。

Sublime Text3でバイト数を表示するプラグイン作った

Codecs33をPackagesに入れないとダメ。
https://github.com/seanliang/Codecs33
あと、ConverToUTF8が入ってた方が嬉しい。

例のごとくOS X 10.9で動作確認。
winは知らない。

こんなイメージ
f:id:kaerouka:20140401235447p:plain

ソース→導入方法→雑記の流れで行きます。

ソース

import sublime, sublime_plugin
import codecs


class ByteLen(sublime_plugin.EventListener):

    def __init__(self):
        # statusに表示表辞書のkey
        self.status_key = 'bytes'
        # エンコーディングが取得できない場合に使用するエンコーディング
        self.default_encodings = ['cp932', 'utf-8']
        # エラー用
        unknown = '?'

    def on_selection_modified_async(self, view):
        text = ''
        for sel in view.sel():
            text += view.substr(sel)

        if not text:
            view.erase_status(self.status_key)
            return

        try:
            # origin_encodingは、ConvertToUTF8プラグインが指定している。
            n = len(codecs.encode(text, view.get_status('origin_encoding')))
        except:
            try:
                # 無理ならデフォルト
                for enc in self.default_encodings:
                    try:
                        n = len(codecs.encode(text, enc))
                    except:
                        pass
                    
                    # バイト数が取得できればelse句に進まずに正常終了
                    if n:
                        break
                else:
                    n = unknown
            except:
                n = unknown

        view.set_status(self.status_key, str(n) + ' bytes')

導入

ソースをbyte_len.pyという名前で、Sublime Text3の基本設定から辿れるPackagesフォルダにぶちこむだけ。
再起動位は必要かも。わかんない。
f:id:kaerouka:20140401235915p:plain
選択した箇所のバイト数が、左下のステータスバーに表示されるようになる。

雑記

なんでこんなプラグイン作ったかって、固定長のファイルを作りたかったからですよ。
レガシーなシステムとのやりとりって、未だに固定長だったりするわけで。
商品コード→38バイト目から30バイト
商品名→68バイト目から100バイト
みたいな仕様で渡ってくるデータが普通にあんの。

行の最後はCRLFで終わったりするんだけど、その前は必ず数字の2が入る!
とかそうゆう謎ルールがあったりする。

で、Sublimeさんは、選択した文字数はカウントしてくれても、バイト数はカウントしてくれない。
困るので作った。
秀丸サクラエディタだと、最初が0バイト目だったり1バイト目だったりして
何がなんだかわからんくなった記憶があったから、このプラグインは選択したバイト数の合計って形にした。

日本語周りでいろいろと足りなさすぎぜSublimeさん・・・

ソースのsublime開発周りを軽く解説

こんかいはsublime_plugin.EventListenerを継承して
on_selection_modified_async関数を実装することで、「何かを選択したらプラグインが動く」みたいなことしてる。
似たような関数でon_selection_modifiedってのがあるんだけど、こっちはまずい。
1文字ずつ連続して選択肢たりするとエラーメッセージと共に落ちる。
on_selection_modified_asyncは別スレッドで実行してくれるらしいので軽快に動作するみたい。
on_selection_modifiedの方は使い道がわからん。

左下のステータスバー関連の関数はこれら。

  1. set_status(key, value)
  2. get_status(key)
  3. erase_status(key)
  4. overwrite_status()
  5. set_overwrite_status(enabled)

ステータスバーに出てるエンコードするのに使った文字コードが欲しかったから
それを取ろうと思ったけど、keyがわからないから取れない。
こうゆうのマジ困る。
しかたないからConvertToUTF8のソース見てキー探してきたorz。

ちなみにencoding()の方はまともに動作しないから使ってない。
ConvertToUTF8使ってるからかしらんけど、いきなりWestern (Windows 1252)とか出てくる。
Sublime Text3の、ファイル→エンコードを指定して上書き保存の一覧に出てくるような文字列。