ctypes入門してみた。

ctypesって?

pythonFFI用のモジュールらしい。
pythonの枠からはみ出た関数をpythonから呼び出すための接触面といった感じのもの.

ドキュメントを見て、動くコードを書いてみた。

やってみたのは以下のこと

  • floatの配列を作ってこれをqsort
  • python側で構造体を作成してこれをqsort

qsortは関数ポインタを使っているし、だいたいの機能を網羅していると思う。((実際には、bitfieldの利用やunionなど使っていない機能はあるけれど、まぁ必要十分だと思う)

実際のコード

最初、qsortに渡す関数の戻り値をintにしていなくてハマったのはないしょ。

floatの配列をqsort

python側で作成した構造体(Point)をqsort

ldconfigとかpkgconfigなどのコマンドも久しぶりに使ってみてだいぶオプションの内容など忘れている。

seq使うのむずかし。(Seq.skipおそい)

まとめ

  • seq式で遊ぼうとした
  • combinationsとか生成すれば良さそう。
  • 全部seqにすると遅い。

seq式で遊ぼうとした

seq式は値の列を返す計算を返してくれる。

> seq {for x in [1..10] do yield x*x} |> Seq.toList;;
val it : int list = [1; 4; 9; 16; 25; 36; 49; 64; 81; 100]
  • 要素を結果として与えてくれるyield
  • 列をconcatしてくれるyield!

の2つがある。

> seq {yield 1; yield 2; yield 3};;
val it : seq<int> = seq [1; 2; 3]

> seq {yield! [1;2;3]; yield 4; yield! [5]};;
val it : seq<int> = seq [1; 2; 3; 4; ...]

combinationsでも生成すれば良さそう

普通に再帰で書いたことはあるけれど、seq式のようなものでまとめて書いたことない。
yield!があるので、全部seq式で書けそう。
書いた。


全部seqにすると遅い。

[1..20]までの列から8個取る組み合わせを列挙してみる

$ time ./permtations.exe

  ...]./permtations.exe  39.82s user 0.81s system 176% cpu 23.075 total

すっごい遅い。

profilerを使ってみる。

monoなので以下のような感じでprofiler使う。

$ mono --profile=default:stat permtations.exe

prof counts: total/unmanaged: 2383/1115
   1014	42.57 % mono()
    349	14.65 % Microsoft.FSharp.Collections.SeqModule/Skip@1428<int>:GenerateNext (System.Collections.Generic.IEnumerable`1<int>&)
    267	11.21 % Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1<int>:MoveNextImpl ()
    109	 4.58 % Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1<int>:System-Collections-IEnumerator-MoveNext ()
     69	 2.90 % (wrapper alloc) object:Alloc (intptr)
     60	 2.52 % Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1<int>:System-Collections-Generic-IEnumerator`1-get_Current ()
     53	 2.23 % Microsoft.FSharp.Core.Operators/OperatorIntrinsics/BaseRangeEnumerator`1<int>:System-Collections-IEnumerator-MoveNext ()
     43	 1.81 % Microsoft.FSharp.Collections.PrivateListHelpers/ListEnumerator`1<int>:System-Collections-IEnumerator-MoveNext ()
     34	 1.43 % (wrapper managed-to-native) object:__icall_wrapper_mono_object_new_fast (intptr)
     34	 1.43 % Microsoft.FSharp.Collections.SeqModule/Skip@1428<int>:Close ()

Seq.skipが遅い。Seq.skipが遅い。

Seq.skip

Seq.skipはdropみたいなもの。N個飛ばした列を返す

> seq {yield 1; yield 2} |> Seq.skip 1;;
val it : seq<int> = seq [2]
> [1;2;3] |> List.tail;;
val it : int list = [2; 3]

Seq.skip使っている箇所をlistに置き換えてみる。


$ time ./permtations2.exe

...]./permtations2.exe  2.63s user 0.11s system 173% cpu 1.575 total

早くなった。

ちなみに

pythonのitertolsを使った場合はこれくらいの速度([:20]はf#の出力に大体合わせるため)

 $ time python -c "import itertools as i; print [x for x in i.combinations(range(20),8)][:20]"                                                                                                                  [~/sandbox/fsharp]
[(0, 1, 2, 3, 4, 5, 6, 7), (0, 1, 2, 3, 4, 5, 6, 8), (0, 1, 2, 3, 4, 5, 6, 9), (0, 1, 2, 3, 4, 5, 6, 10), (0, 1, 2, 3, 4, 5, 6, 11), (0, 1, 2, 3, 4, 5, 6, 12), (0, 1, 2, 3, 4, 5, 6, 13), (0, 1, 2, 3, 4, 5, 6, 14), (0, 1, 2, 3, 4, 5, 6, 15), (0, 1, 2, 3, 4, 5, 6, 16), (0, 1, 2, 3, 4, 5, 6, 17), (0, 1, 2, 3, 4, 5, 6, 18), (0, 1, 2, 3, 4, 5, 6, 19), (0, 1, 2, 3, 4, 5, 7, 8), (0, 1, 2, 3, 4, 5, 7, 9), (0, 1, 2, 3, 4, 5, 7, 10), (0, 1, 2, 3, 4, 5, 7, 11), (0, 1, 2, 3, 4, 5, 7, 12), (0, 1, 2, 3, 4, 5, 7, 13), (0, 1, 2, 3, 4, 5, 7, 14)]
python -c   0.07s user 0.01s system 93% cpu 0.086 total

cat

  • EntryPointというattributeをつけるとコマンドライン引数を受け取る関数として扱われる
  • openでmodule(namespace)のopen
  • (System.IO.File.Readlinesは環境が古くて無かったっぽい)
  • オーバーロードされたメソッドを利用する時には型注釈必要。
open System

let lines (name:string) = 
    seq { use sr = new IO.StreamReader(name)
          while not sr.EndOfStream do
          yield sr.ReadLine()
    }   

let cat (name:string) =
    lines name
    |> Seq.iter (fun (x:string) -> Console.WriteLine x)

[<EntryPoint>]
let main (args:string[]) =
    args |> Seq.iter cat
    0
$ fsc.exe cat.fs
$ cat.exe cat.fs

f#の環境構築(ubuntu)

作業

  • f#のインストール
  • emacsの環境作成(fsharp-mode)
  • F#を利用してみる

f#のインストール

大体はこのURLのページのとおりに作成。

sudo aptitude install mono-devel mono-tools-devel
libmono-winforms2.0-cil libmono-system-runtime2.0-cil

pwd # /var/project
wget http://download.microsoft.com/download/6/B/6/6B6BFB83-3D3A-467C-8080-01F7A953A37F/fsharp.zip

unzip fsharp.zip
cd FSharp-2.0.0.0/bin

echo "export MONO_PATH=`pwd`:\$MONO_PATH" >> ~/.zshrc
echo "export PATH=`pwd`:\$PATH" >> ~/.zshrc
chmod u+x *.exe

インストールするF#のversionが古かったので、新しい方を利用することにした。
ダウンロードしたzipファイルを展開したディレクトリのREADMEに従う必要は無かった。

emacsの環境作成(fsharp-mode)

以下のリンクから取得する。((sourceforgeからのダウンロードは既に面倒な作業になってきている。(githubなどの方が楽)))
http://sourceforge.net/projects/fsharp-mode/files/latest/download

unzip fsharp-0.3.zip
mv fsharp fsharp-mode
editor fsharp-mode/init.el

fsharp-modeを有効にするための設定を追加(init.el)

;; if current-directory is not found. use `default-directory'
(add-to-list 'load-path (current-directory))
(add-to-list 'auto-mode-alist '("\\.fs[iylx]?$" . fsharp-mode))

(autoload 'fsharp-mode "fsharp" "Major mode for editing F# code." t)
(autoload 'run-fsharp "inf-fsharp" "Run an inferior F# process." t)

これでemacsの環境は整った。

f#を利用してみる。

hello world(compile)

echo 'printf "hey" ' >> hello.fs
fsc.exe hello.fs
./hello.exe

動いた。

githubにパッケージ挙げる時のディレクトリ構成

悩まなくても良いようにメモ。

作りたいパッケージ(パッケージ名: foo)

ファイル構成(置き場所)

.
├── README.txt
├── docs
├── foo
│   ├── __init__.py
│   ├── locale
│   └── tests
├── setup.cfg
├── setup.py
└── tox.ini

シンプルなもの

.
├── README.txt
├── foo
│   ├── __init__.py
│   └── tests
├── setup.cfg
└── setup.py
  • 普通にsetup.pyを書く
  • setup.pyにextra_requiersを書く(docs,testing)
  • setup.cfgにextra_requiresのためのaliasを書く

setup.py,setup.cfg, extra_require (not using pip)

e.g. colander
setup.py

##... snip

testing_extras = ['nose', 'coverage']
docs_extras = ['Sphinx']

setup(name='colander',
      version='0.9.8',
## ... snip
      install_requires = requires,
      test_suite="colander",
      extras_require = {
          'testing':testing_extras,
          'docs':docs_extras,
          },
      )

setup.cfg

## ... snip
[aliases]
dev = develop easy_install colander[testing]
docs = develop easy_install colander[docs]
## .. snip

how to use

$ python setup.py dev # install package include testing requirements
$ python setup.py docs # instapp package for documantation