Cousera Machine Learning Course 修了
CouseraのMachine Learningコースを修了した。iPhoneのアプリもフルに活用していたので、修了までの期間は始めてから1ヶ月ちょっとくらい。このコースにはQuizとAssignmentがあって必要な点数を取らないと修了とみなされないのだが、Octaveを使った行列計算が必須になるため、それに慣れてなくて思わぬところで時間を食うことになったり。自信が無いところがあるとそこにばかり気を使ってなかなか正解に至らないのだが、結局間違っていたのはそんなところではなくいつも通りやっていればそれほど時間をかけずに解答できただろうという問題もいくつかあった。機械学習の基礎的な知識が身に付いたのはもちろん、行列計算における大事な要素を手が覚えるくらいしっかり理解することができた。内容はあくまでも実践に重きを置いているので、誤差逆伝搬法の導出や、SVMの双対問題、主成分分析の特異値分解、などの詳細に踏み入れることなく比較的カジュアルにコースを進めていける。
- 作者: 岡谷貴之
- 出版社/メーカー: 講談社
- 発売日: 2015/04/08
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (9件) を見る
ズンドコキヨシをErlangのgen_fsmを使って書いた
- FSMが保持するStateDataを使わないバージョンを追加
そういえばズンドコキヨシは gen_fsm のイイサンプルになるとおもうので、誰か書きませんか。
— V (@voluntas) March 18, 2016
ということで書いてみた。graceful exitがなかなか思うようにいかなかったし、コールバック関数も全部実装していないのでコンパイル時にWarningが出たまんま。Unicodeのformatは~ts
、get_stateは文字列の比較で頑張ってたけど、ちゃんとatomが返ってきてた*1。そもそもget_state使うべきでは無い気がするけど、graceful exitがうまく行かなかったのでこのやり方としている。
-module(zundokokiyoshi). -behaviour(gen_fsm). -compile(export_all). start_link(COMP) -> gen_fsm:start_link({local, ?MODULE}, ?MODULE, lists:reverse(COMP), []). init(COMP) -> {ok, zundoko, {[], COMP}}. finish_song() -> gen_fsm:send_all_state_event(?MODULE, stop). sing_phrase(Phrase) -> gen_fsm:send_event(?MODULE, {sing, Phrase}). zundoko({sing, Phrase}, {SoFar, COMP}) -> case lists:sublist([Phrase|SoFar],5) of COMP -> io:format("ドコ~nキヨシ!~n"), {next_state, finish, {[], COMP}}; ["ドコ"|_Tail] -> io:format("ドコ~n"), {next_state, zundoko, {[], COMP}}; Else -> io:format("~ts~n",[Phrase]), {next_state, zundoko, {Else, COMP}} end. handle_event(stop, _StateName, StateData) -> {stop, normal, StateData}. terminate(normal, _StateName, _StateData) -> ok. take_phrase() -> Zd = ["ズン","ドコ"], Index = random:uniform(length(Zd)), lists:nth(Index,Zd). start_song() -> start_link(["ズン","ズン","ズン","ズン","ドコ"]), random:seed( os:timestamp() ), sing_song(). sing_song() -> sing_phrase( take_phrase() ), timer:sleep(200), case sys:get_state(?MODULE) of {finish,_} -> finish_song(); _ -> sing_song() end.
- SoFarを使わずに全て状態遷移で実現(こちらの方がFSMを使う良い例ぽい)
-module(zundokokiyoshi). -behaviour(gen_fsm). -compile(export_all). start_link(COMP) -> gen_fsm:start_link({local, ?MODULE}, ?MODULE, lists:reverse(COMP), []). init(COMP) -> {ok, zun1, {[], COMP}}. finish_song() -> gen_fsm:send_all_state_event(?MODULE, stop). sing_phrase(Phrase) -> io:format("~ts~n",[Phrase]), gen_fsm:send_event(?MODULE, {sing, Phrase}). zun1({sing, Phrase}, {_, COMP}) -> case Phrase of "ズン" -> {next_state, zun2, {[], COMP}}; "ドコ" -> {next_state, zun1, {[], COMP}} end. zun2({sing, Phrase}, {_, COMP}) -> case Phrase of "ズン" -> {next_state, zun3, {[], COMP}}; "ドコ" -> {next_state, zun1, {[], COMP}} end. zun3({sing, Phrase}, {_, COMP}) -> case Phrase of "ズン" -> {next_state, zun4, {[], COMP}}; "ドコ" -> {next_state, zun1, {[], COMP}} end. zun4({sing, Phrase}, {_, COMP}) -> case Phrase of "ズン" -> {next_state, doko, {[], COMP}}; "ドコ" -> {next_state, zun1, {[], COMP}} end. doko({sing, Phrase}, {_, COMP}) -> case Phrase of "ズン" -> {next_state, doko, {[], COMP}}; "ドコ" -> io:format("キヨシ!~n"), {next_state, finish, {[], COMP}} end. handle_event(stop, _StateName, StateData) -> {stop, normal, StateData}. terminate(normal, _StateName, _StateData) -> ok. take_phrase() -> Zd = ["ズン","ドコ"], Index = random:uniform(length(Zd)), lists:nth(Index,Zd). start_song() -> start_link(["ズン","ズン","ズン","ズン","ドコ"]), random:seed( os:timestamp() ), sing_song(). sing_song() -> sing_phrase( take_phrase() ), timer:sleep(200), case sys:get_state(?MODULE) of {finish,_} -> finish_song(); _ -> sing_song() end.
$ erl Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] Eshell V7.1 (abort with ^G) 1> c(zundokokiyoshi). zundokokiyoshi.erl:2: Warning: undefined callback function code_change/4 (behaviour 'gen_fsm') zundokokiyoshi.erl:2: Warning: undefined callback function handle_info/3 (behaviour 'gen_fsm') zundokokiyoshi.erl:2: Warning: undefined callback function handle_sync_event/4 (behaviour 'gen_fsm') {ok,zundokokiyoshi} 2> zundokokiyoshi:start_song(). ドコ ズン ドコ ドコ ズン ズン ドコ ズン ズン ズン ドコ ドコ ズン ズン ドコ ズン ドコ ドコ ズン ズン ズン ドコ ズン ズン ズン ズン ドコ キヨシ! ok 3>
*1:これ見る人が見るとStateはatomだと分かるのだろうか?センスの問題かもしれないけれど。 http://erlang.org/doc/man/sys.html#get_state-1
Pandas DataFrame について
Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理
- 作者: Wes McKinney,小林儀匡,鈴木宏尚,瀬戸山雅人,滝口開資,野上大介
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/12/26
- メディア: 大型本
- この商品を含むブログ (12件) を見る
PandasのDataFrameは行列形式のデータ構造で、indexやcolumnにkeyが指定できたり*1、indexやcolumnをまとめたものにnameを付けられたりする。DataFrameに対する集計関数や条件選択などはある程度直感的に操作できるが、Python標準のデータ構造から階層的なindexを持ったDataFrameを作成する場合や、階層的なindexを持ったDataFrameをどのように自由に変換できるのか、などが分かりづらかったので色々試してみた。
Nested dictを元にDataFrameを作成する場合、一番外側のkeyがcolumn、次に内側のkeyがindex、となってDataFrameを作成してくれる。ただし、tupleをkeyに使ったdictを元にDataFrameを作成する場合は、indexが無いことになるので、indexを指定しないとValueError: If using all scalar values, you must pass an index
というエラーになる。そのため、indexを指定するか、あるいは値をscalarではなくlistとするか、が必要になる(df5とdf6の違い)。*2
$ python dataframe_run.py df1 col1 col2 col3 idx1 0 4 8 idx2 1 5 9 idx3 2 6 10 idx4 3 7 11 df2 col1 col2 col3 idx1 0 4 8 idx2 1 5 9 idx3 2 6 10 idx4 3 7 11 df1 == df2 col1 col2 col3 idx1 True True True idx2 True True True idx3 True True True idx4 True True True df3(MultiIndex) #1 col1 col2 col3 idx1-1 idx2-1 idx3-1 0 1 2 idx1-2 idx2-2 idx3-2 3 4 5 idx2-3 idx3-3 6 7 8 idx3-4 9 10 11 df3(MultiIndex) #2: Append column col1 col2 col3 col4 idx1-1 idx2-1 idx3-1 0 1 2 100 idx1-2 idx2-2 idx3-2 3 4 5 100 idx2-3 idx3-3 6 7 8 100 idx3-4 9 10 11 100 df3(MultiIndex) #3: Access to scalar value 10 df3(MultiIndex) #4: Access to scalar by ix property 11 df3(MultiIndex) #5: Access to record by ix with condtions col1 col3 idx1-2 idx2-2 idx3-2 3 5 idx2-3 idx3-3 6 8 df3(MultiIndex) #6: Access to record by ix with conds specified by isin method col2 col3 idx1-1 idx2-1 idx3-1 1 2 idx1-2 idx2-3 idx3-3 7 8 df3(MultiIndex) #7: Pivot table with fillna method col3 2 5 8 11 col2 1 0 -1 -1 -1 4 -1 3 -1 -1 7 -1 -1 6 -1 10 -1 -1 -1 9 df3(MultiInex) #8: Stack method idx1-1 idx2-1 idx3-1 col1 0 col2 1 col3 2 col4 100 idx1-2 idx2-2 idx3-2 col1 3 col2 4 col3 5 col4 100 idx2-3 idx3-3 col1 6 col2 7 col3 8 col4 100 idx3-4 col1 9 col2 10 col3 11 col4 100 dtype: int64 df3(MultiIndex) #9: Stack and unstack col1 col2 col3 col4 idx1-1 idx2-1 idx3-1 0 1 2 100 idx1-2 idx2-2 idx3-2 3 4 5 100 idx2-3 idx3-3 6 7 8 100 idx3-4 9 10 11 100 df3(MultiIndex) #10: Unstack method col1 col2 col3 \ idx3-1 idx3-2 idx3-3 idx3-4 idx3-1 idx3-2 idx3-3 idx3-4 idx3-1 idx1-1 idx2-1 0 NaN NaN NaN 1 NaN NaN NaN 2 idx1-2 idx2-2 NaN 3 NaN NaN NaN 4 NaN NaN NaN idx2-3 NaN NaN 6 9 NaN NaN 7 10 NaN col4 idx3-2 idx3-3 idx3-4 idx3-1 idx3-2 idx3-3 idx3-4 idx1-1 idx2-1 NaN NaN NaN 100 NaN NaN NaN idx1-2 idx2-2 5 NaN NaN NaN 100 NaN NaN idx2-3 NaN 8 11 NaN NaN 100 100 df3(MultiIndex) #11: Nested unstacks col1 \ idx3-1 idx3-2 idx3-3 idx3-4 idx2-1 idx2-2 idx2-3 idx2-1 idx2-2 idx2-3 idx2-1 idx2-2 idx2-3 idx2-1 idx1-1 0 NaN NaN NaN NaN NaN NaN NaN NaN NaN idx1-2 NaN NaN NaN NaN 3 NaN NaN NaN 6 NaN ... col4 \ ... idx3-1 idx3-2 idx3-3 idx3-4 ... idx2-3 idx2-1 idx2-2 idx2-3 idx2-1 idx2-2 idx2-3 idx2-1 idx2-2 idx1-1 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN idx1-2 ... NaN NaN 100 NaN NaN NaN 100 NaN NaN idx2-3 idx1-1 NaN idx1-2 100 [2 rows x 48 columns] df3(MultiIndex) #12: Swaplevel method col1 col2 col3 col4 idx3-1 idx2-1 idx1-1 0 1 2 100 idx3-2 idx2-2 idx1-2 3 4 5 100 idx3-3 idx2-3 idx1-2 6 7 8 100 idx3-4 idx2-3 idx1-2 9 10 11 100 df4(Nested dict) a1 a2 \ b1 {'c3': 2, 'c1': 0, 'c2': 1} {'c3': 11, 'c1': 9, 'c2': 10} b2 {'c3': 5, 'c1': 3, 'c2': 4} {'c3': 14, 'c1': 12, 'c2': 13} b3 {'c3': 8, 'c1': 6, 'c2': 7} {'c3': 17, 'c1': 15, 'c2': 16} a3 b1 {'c3': 20, 'c1': 18, 'c2': 19} b2 {'c3': 23, 'c1': 21, 'c2': 22} b3 {'c3': 26, 'c1': 24, 'c2': 25} df5(Dict with tuple key) #1 a1 a2 ... a3 b1 b2 b3 b1 ... b3 b1 b2 b3 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 ... c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 0 1 2 3 4 5 6 7 8 9 ... 17 18 19 20 21 22 23 24 25 26 [1 rows x 27 columns] df5(Dict with tuple key) #2: Stack method a1 a2 a3 b1 b2 b3 b1 b2 b3 b1 b2 b3 c1 0 3 6 9 12 15 18 21 24 c2 1 4 7 10 13 16 19 22 25 c3 2 5 8 11 14 17 20 23 26 df5(Dict with tuple key) #3: Nested stacks c1 b1 a1 0 a2 9 a3 18 b2 a1 3 a2 12 a3 21 b3 a1 6 a2 15 a3 24 c2 b1 a1 1 a2 10 a3 19 b2 a1 4 a2 13 a3 22 b3 a1 7 a2 16 a3 25 c3 b1 a1 2 a2 11 a3 20 b2 a1 5 a2 14 a3 23 b3 a1 8 a2 17 a3 26 dtype: int64 df5(Dict with tuple key) #4: Nested stacks and swaplevel a1 b1 c1 0 a2 b1 c1 9 a3 b1 c1 18 a1 b2 c1 3 a2 b2 c1 12 a3 b2 c1 21 a1 b3 c1 6 a2 b3 c1 15 a3 b3 c1 24 a1 b1 c2 1 a2 b1 c2 10 a3 b1 c2 19 a1 b2 c2 4 a2 b2 c2 13 a3 b2 c2 22 a1 b3 c2 7 a2 b3 c2 16 a3 b3 c2 25 a1 b1 c3 2 a2 b1 c3 11 a3 b1 c3 20 a1 b2 c3 5 a2 b2 c3 14 a3 b2 c3 23 a1 b3 c3 8 a2 b3 c3 17 a3 b3 c3 26 dtype: int64 df5(Dict with tuple key) #5: Nested stacks, swaplevel and unstack c1 c2 c3 a1 b1 0 1 2 b2 3 4 5 b3 6 7 8 a2 b1 9 10 11 b2 12 13 14 b3 15 16 17 a3 b1 18 19 20 b2 21 22 23 b3 24 25 26 df5(Dict with tuple key) #6: Swaplevel with axis=1 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 ... c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 b1 b1 b1 b2 b2 b2 b3 b3 b3 b1 ... b3 b1 b1 b1 b2 b2 b2 b3 b3 b3 a1 a1 a1 a1 a1 a1 a1 a1 a1 a2 ... a2 a3 a3 a3 a3 a3 a3 a3 a3 a3 0 1 2 3 4 5 6 7 8 9 ... 17 18 19 20 21 22 23 24 25 26 [1 rows x 27 columns] df6(Dict with tuple key and list values) a1 a2 ... a3 b1 b2 b3 b1 ... b3 b1 b2 b3 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 ... c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 0 0 1 2 3 4 5 6 7 8 9 ... 17 18 19 20 21 22 23 24 25 26 [1 rows x 27 columns]
PythonでPandasのPlotを試す
こちらの記事のオマージュです。環境は、Windows/Python3.4です。pandasなどはpipでインストールします。groupbyによる集計や、日本語表記などに対応しました。
補記:可変長引数・キーワード可変長引数
$ cat kwargs.py def func(x,a=1,z=0,zz=100,*args,**kwargs): print("x=",x) print("=====") print("a=",a) print("=====") print("args=",args) print("=====") print("kwargs",kwargs) print("=====") print("z=",z) print("=====") print("zz=",zz) args = [10,20,30,40,50] kargs = {"a":10,"b":20,"c":30,"d":40,"e":50} print("-----func(args)-----") func(args) print("-----func(*args)-----") func(*args) print("-----func(1,**kargs)-----") func(1,**kargs) print("-----func(1,*kargs)-----") func(1,*kargs) $ python kwargs.py -----func(args)----- x= [10, 20, 30, 40, 50] ===== a= 1 ===== args= () ===== kwargs {} ===== z= 0 ===== zz= 100 -----func(*args)----- x= 10 ===== a= 20 ===== args= (50,) ===== kwargs {} ===== z= 30 ===== zz= 40 -----func(1,**kargs)----- x= 1 ===== a= 10 ===== args= () ===== kwargs {'d': 40, 'c': 30, 'e': 50, 'b': 20} ===== z= 0 ===== zz= 100 -----func(1,*kargs)----- x= 1 ===== a= e ===== args= ('a', 'd') ===== kwargs {} ===== z= c ===== zz= b
実引数の辞書に*
を付けて関数に渡すと、辞書のキーが仮引数に渡される。仮引数のデフォルト引数への割り当ては、仮引数の宣言順序とは関係なく実施される。以下のように、実行するたびに割り当てが変わる。
-----func(1,*kargs)----- x= 1 ===== a= b ===== args= ('c', 'd') ===== kwargs {} ===== z= e ===== zz= a
splitlines()
splitlines()は最後の空白要素を削除するが、split("\n")は削除しない。True
を与えると改行を付けたまま分割して返す。
>>> a="aaa\nbbb\nccc\n" >>> a.splitlines() ['aaa', 'bbb', 'ccc'] >>> a.split("\n") ['aaa', 'bbb', 'ccc', ''] >>> a="aaa\nbbb\nccc\n\n\n\n" >>> a.splitlines() ['aaa', 'bbb', 'ccc', '', '', ''] >>> a.split("\n") ['aaa', 'bbb', 'ccc', '', '', '', ''] >>> a="aaa\nbbb\nccc" >>> a.splitlines() ['aaa', 'bbb', 'ccc'] >>> a.split("\n") ['aaa', 'bbb', 'ccc'] >>> a.splitlines(True) ['aaa\n', 'bbb\n', 'ccc']
Cythonによるパフォーマンス向上
- 作者: Kurt W. Smith,中田秀基,長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/06/19
- メディア: 大型本
- この商品を含むブログ (3件) を見る
Pythonのような書きやすいインタープリタ型言語で書き始めて結果を取得できるところまでいくと、その後にはパフォーマンスを上げるためにC++で書きなおそうか、と思ったりする。かといって最初からC++で書いていては、きちんと動くものを書き切るのに間違いなく時間がかかるし、とにかくまず結果が欲しいときには取るべき有効な手段ではないと思う。
そこでCythonの出番。Pythonのコードはそのままで有効なCythonのコードでもあるので、cythonをインストールして拡張子をpyx
に変更し、import pyximport; pyximport.install()
をつけるだけで速度改善の効果が望める。もちろん、静的な型指定などのチューニングを施すことによって、Pythonのセマンティクスを保ったまま、よりCに近い速度を達成することができる。これだけ簡単に導入できるので、もしCPUバウンドな処理であることが分かっているなら、Cythonを使わない手はないだろう。
どのような場合にどの程度の速度改善が望めるか、以下のようにPythonのコードを実行した結果と、PythonのコードをそのままCythonとしてコンパイルして実行した結果、Cythonとして書き直したコードを実行した結果、を比較した。プログラムの内容は、単にソートを実施しているだけだが、再帰呼び出しの有り・無し、でそれぞれプログラムを変えている。Cythonでは、stdlib.h
のqsort
を使っている。
再帰呼び出し有り、では関数呼び出しのオーバーヘッドが大きくなるため、PythonのコードをそのままCythonとしてコンパイルして実行した結果、が、Pythonのコードを実行した結果に比べて、速度改善率が高い。
再帰呼び出し無し、では、ソートするリストの長さを1000倍しており、Cythonのコードの速度改善率がかなり高い。
このようにある特定のモジュールをCython化するだけでも、かなり速度改善が望めることが分かる。Cそのもののコードを書かずにPythonスクリプトの書きやすさを保ったままパフォーマンス向上が可能となる。あとは、プロファイラーを使ってCython化すべき箇所を特定する作業が必要になるだろう。
再帰呼び出し有り
$ cat pyqsort.py def pyqsort(x): if len(x)==1: return array = sorted(x) pyqsort(x[:-1])
$ cat cyqsort.pyx def cyqsort(x): if len(x)==1: return array = sorted(x) cyqsort(x[:-1])
$ cat cqsort.pyx cdef extern from "stdlib.h": void qsort(void *array,size_t count,size_t size,int (*compare)(const void*,const void*)) void *malloc(size_t size) void free(void *ptr) def cqsort(list x): cdef: int *array int i, N N = len(x) if N==1: return array = <int*>malloc(sizeof(int) * N) if array == NULL: raise MemoryError("Unable to allocate array.") for i in range(N): array[i] = x[i] qsort(<void*>array, <size_t>N, sizeof(int), int_compare) free(array) cqsort(x[:-1]) cdef int int_compare(const void *a, const void *b): cdef int ia, ib ia = (<int*>a)[0] ib = (<int*>b)[0] return ia-ib
$ ipython --no-banner In [1]: from pyqsort import pyqsort In [2]: from random import shuffle In [3]: x=list(range(100)) In [4]: shuffle(x) In [5]: %timeit pyqsort(x) 1000 loops, best of 3: 642 µs per loop In [6]: import pyximport; pyximport.install() Out[6]: (None, <pyximport.pyximport.PyxImporter at 0x106dc1eb8>) In [7]: from cyqsort import cyqsort In [8]: %timeit cyqsort(x) 1000 loops, best of 3: 555 µs per loop In [9]: from cqsort import cqsort In [10]: %timeit cqsort(x) 1000 loops, best of 3: 349 µs per loop In [11]:
再帰呼び出し無し
$ cat pyqsort_once.py def pyqsort(x): array = sorted(x)
$ cat cyqsort_once.pyx def cyqsort(x): array = sorted(x)
$ cat cqsort_once.pyx cdef extern from "stdlib.h": void qsort(void *array,size_t count,size_t size,int (*compare)(const void*,const void*)) void *malloc(size_t size) void free(void *ptr) def cqsort(list x): cdef: int *array int i, N N = len(x) if N==1: return array = <int*>malloc(sizeof(int) * N) if array == NULL: raise MemoryError("Unable to allocate array.") for i in range(N): array[i] = x[i] qsort(<void*>array, <size_t>N, sizeof(int), int_compare) free(array) cdef int int_compare(const void *a, const void *b): cdef int ia, ib ia = (<int*>a)[0] ib = (<int*>b)[0] return ia-ib
$ ipython --no-banner In [1]: from pyqsort_once import pyqsort In [2]: from random import shuffle In [3]: x=list(range(1000000)) In [4]: shuffle(x) In [5]: %timeit pyqsort(x) 1 loop, best of 3: 760 ms per loop In [6]: import pyximport; pyximport.install() Out[6]: (None, <pyximport.pyximport.PyxImporter at 0x10c9bbc88>) In [7]: from cyqsort_once import cyqsort In [8]: %timeit cyqsort(x) 1 loop, best of 3: 759 ms per loop In [9]: from cqsort_once import cqsort In [10]: %timeit cqsort(x) 1 loop, best of 3: 193 ms per loop In [11]:
ループで呼び出し
$ ipython --no-banner In [1]: from pyqsort_once import pyqsort In [2]: from random import shuffle In [3]: x=list(range(1000)) In [4]: shuffle(x) In [5]: %timeit for i in range(1000): pyqsort(x) 1 loop, best of 3: 218 ms per loop In [6]: import pyximport; pyximport.install() Out[6]: (None, <pyximport.pyximport.PyxImporter at 0x10dd583c8>) In [7]: from cyqsort_once import cyqsort In [8]: %timeit for i in range(1000): cyqsort(x) 1 loop, best of 3: 218 ms per loop In [9]: from cqsort_once import cqsort In [10]: %timeit for i in range(1000): cqsort(x) 10 loops, best of 3: 84.8 ms per loop In [11]:
Pythonの正規表現でバックスラッシュを含む文字列とマッチする場合
Pythonのreモジュールでsearchなどの関数に正規表現を与える場合、rubyやperlなどのように正規表現を直接与えることができず、文字列として与える必要があるため、WindowsのPath区切りにマッチさせたいときなどに正規表現内にバックスラッシュを使いたい場合、一つのバックスラッシュに対して"\\\\"
とする必要がある。r
プレフィックスを用いることで、文字列としてのエスケープは行われなくなるので、正規表現としての一つのバックスラッシュをr"\\"
と書ける。 ただし文字列の最後にr"\"
とは書けないことにも注意が必要。
reモジュールのsearch関数の第1引数は正規表現、第2引数はマッチ対象となる文字列だ。
>>> import re >>> re.search("abc","abc") <_sre.SRE_Match object; span=(0, 3), match='abc'> >>> re.search(r"abc\\abc","abc\\abc") <_sre.SRE_Match object; span=(0, 7), match='abc\\abc'> >>> re.search("abc\\\\abc","abc\\abc") <_sre.SRE_Match object; span=(0, 7), match='abc\\abc'> >>> re.search(r"abc\\\\abc",r"abc\\abc") <_sre.SRE_Match object; span=(0, 8), match='abc\\\\abc'> >>> re.search(r"abc\\abc",r"abc\abc") <_sre.SRE_Match object; span=(0, 7), match='abc\\abc'> >>> re.search(r"abc\\abc\\",r"abc\abc\") File "<stdin>", line 1 re.search(r"abc\\abc\\",r"abc\abc\") ^ SyntaxError: EOL while scanning string literal
>>> re.search(r"abc\\abc\\","abc\\abc\\") <_sre.SRE_Match object; span=(0, 8), match='abc\\abc\\'>
以下のように正規表現の文字列として"\\"
を与えてしまうと、文字列内でエスケープした結果として\
が正規表現として与えられてしまいエラーとなる。\
は単独では正規表現として有効ではない。
>>> re.search("abc\\","abc\\") Traceback (most recent call last): File "/usr/local/Cellar/python3/3.4.3_2/Frameworks/Python.framework/Versions/3.4/lib/python3.4/sre_parse.py", line 206, in __next c = self.string[self.index + 1] IndexError: string index out of range ... >>> re.search("\\","") Traceback (most recent call last): File "/usr/local/Cellar/python3/3.4.3_2/Frameworks/Python.framework/Versions/3.4/lib/python3.4/sre_parse.py", line 206, in __next c = self.string[self.index + 1] IndexError: string index out of range ...
バックスラッシュを含む文字列が変数に代入されており、それを正規表現として扱いたい場合、文字列中のバックスペースは二回エスケープされることになるので、予め"\\\\"
とするか、あるいはrプレフィックスを与えてr"\\"
としておく必要がある。
>>> a="abc\\\\abc" >>> a 'abc\\\\abc' >>> re.search(a,"abc\\abc") <_sre.SRE_Match object; span=(0, 7), match='abc\\abc'> >>> a=r"abc\\abc" >>> a 'abc\\\\abc' >>> re.search(a,"abc\\abc") <_sre.SRE_Match object; span=(0, 7), match='abc\\abc'>
以下は誤ったやり方になる。上記の通り、rプレフィックスは文字列としてのエスケープを行わない、という指示になるので、新たにrプレフィックスを付けた文字列に変数をフォーマットしても期待通りには動かない。また、%r
でフォーマットし直してもうまくいかない。どうにかして(正規表現として使われることを意識してしない)元の変数をsearchの第1引数に与えようと、repl
やeval
を試してみてもうまくいかない。
>>> a="abc\\abc" >>> re.search(a,"abc\\abc") >>> re.search(a,"abc\abc") <_sre.SRE_Match object; span=(0, 6), match='abc\x07bc'> >>> r"{0}".format(a) 'abc\\abc' >>> "%r"%a "'abc\\\\abc'" >>> re.search("%r"%a,"abc\\abc") >>>
>>>repr(a) "'abc\\\\abc'" >>> eval(repr(a)) 'abc\\abc'
そのため、以下のようにsub関数による置換が必要になるかもしれない。
>>> a="abc\\abc" >>> a=re.sub(r"\\",r"\\\\",a) >>> a 'abc\\\\abc' >>> re.search(a,"abc\\abc") <_sre.SRE_Match object; span=(0, 7), match='abc\\abc'>
stroll(nicer walk)のC++バージョンを書いた
ディレクトリを幅優先探索で掘っていき、ディレクトリやファイルに対する条件を適用し、それらを与えた条件(関数)によってソートし、見つけたファイルパスを返すライブラリを書いた。ちょうどPythonのos.walkを高機能にしたもので、所望のファイルだけを取り出したいときに使える。
boost不要で、C++11/C++14も不要なので、古いバージョンのg++(clang)でもコンパイルできる(はず)。Cでファイル処理を書くことはほとんど無いはずなので、C++で使えれば良いかなと。CとC++がごっちゃになっていてなんとなく居心地が悪いけどこんなもんなのだろうか。ちなみに、directory_iteratorは#incude <filesystem>
が必要でMac(clang)だとコンパイルできないようなので、Cを使うことにした。
正規表現マッチをする際に、matchとsearchの違いを忘れてしまいがち。matchは先頭からマッチするが、searchはどこでもマッチする。
$ cat main.cpp #include <iostream> #include "stroll.cpp" using namespace std; using namespace stroll; void ex1(){ cout<<"=== ex1 (mtime) ==="<<endl; Stroll stroll = Stroll("basedir"); stroll.set_sortby(sortByMtime); while(stroll.read()){ cout<< stroll.get() << endl; } } void ex2(){ cout<<"=== ex2 (mtime/reverse) ==="<<endl; Stroll stroll = Stroll("basedir"); stroll.set_sortby(sortByMtimeDesc); while(stroll.read()){ cout<< stroll.get() << endl; } } void ex3(){ cout<<"=== ex3 (no_file) ==="<<endl; vstr no_file; no_file.push_back("1$"); no_file.push_back("2$"); Stroll stroll = Stroll("basedir"); stroll.set_no_file(no_file); while(stroll.read()){ cout<< stroll.get() << endl; } } void ex4(){ cout<<"=== ex4 (yes_path) ==="<<endl; vstr yes_path; yes_path.push_back("1$"); Stroll stroll = Stroll("basedir"); stroll.set_yes_path(yes_path); while(stroll.read()){ cout<< stroll.get() << endl; } } void ex5(){ cout<<"=== ex5 (no_root/len/reverse) ==="<<endl; vstr no_root; no_root.push_back("2$"); Stroll stroll = Stroll("basedir"); stroll.set_no_root(no_root); stroll.set_sortby(sortByLenDesc); while(stroll.read()){ cout<< stroll.get() << endl; } } void ex6(){ cout<<"=== ex6 (yes_file/yes_root) ==="<<endl; vstr yes_file; yes_file.push_back("2$"); vstr yes_root; yes_root.push_back("2$"); Stroll stroll = Stroll("basedir"); stroll.set_yes_file(yes_file); stroll.set_yes_root(yes_root); while(stroll.read()){ cout<< stroll.get() << endl; } } int main(int argc, char* argv[]){ ex1(); ex2(); ex3(); ex4(); ex5(); ex6(); return 0; }
$ cat Makefile main:main.o g++ -L./ -lstroll -o main main.o main.o:libstroll.a g++ -c main.cpp libstroll.a:stroll.o ar r libstroll.a stroll.o stroll.o:stroll.cpp g++ -Wall -O2 -c stroll.cpp .PHONY: clean clean: rm -f libstroll.a stroll.o main
$ ./main === ex1 (mtime) === basedir/file0 basedir/dir1/file11 basedir/dir1/file12 basedir/dir1/file13 basedir/dir1/file111 basedir/dir1/file1111 basedir/dir2/file21 basedir/dir2/file22 basedir/dir2/file23 basedir/dir2/file222 basedir/dir2/file2222 basedir/dir3/file31 basedir/dir3/file32 basedir/dir3/file33 basedir/dir3/file333 basedir/dir3/file3333 === ex2 (mtime/reverse) === basedir/file0 basedir/dir3/file3333 basedir/dir3/file333 basedir/dir3/file33 basedir/dir3/file32 basedir/dir3/file31 basedir/dir2/file2222 basedir/dir2/file222 basedir/dir2/file23 basedir/dir2/file21 basedir/dir2/file22 basedir/dir1/file1111 basedir/dir1/file111 basedir/dir1/file13 basedir/dir1/file12 basedir/dir1/file11 === ex3 (no_file) === basedir/file0 basedir/dir1/file13 basedir/dir2/file23 basedir/dir3/file33 basedir/dir3/file333 basedir/dir3/file3333 === ex4 (yes_path) === basedir/file0 basedir/dir1/file11 basedir/dir1/file111 basedir/dir1/file1111 basedir/dir1/file12 basedir/dir1/file13 === ex5 (no_root/len/reverse) === basedir/file0 basedir/dir1/file1111 basedir/dir1/file111 basedir/dir1/file11 basedir/dir1/file12 basedir/dir1/file13 basedir/dir3/file3333 basedir/dir3/file333 basedir/dir3/file31 basedir/dir3/file32 basedir/dir3/file33 === ex6 (yes_file/yes_root) === basedir/dir2/file22 basedir/dir2/file222 basedir/dir2/file2222