instaviz
Table of Contents
iota and range
前前回だったか、pythonでファンクション プログラミングをやった時、 schemeのiotaって関数を使った。それに対抗してpythonではrangeを利用した。 両者微妙に目指す所が違う。
どんな風に実現されてるか、簡潔なhaskellで確認してみる。ソースも簡単に 参照したいので、25年前のシステムである、hugs98を使う。猫も杓子もghcじゃ、 大袈裟すぎるからね。こやつ、debianならサポートされてるよ。
take :: Int -> [a] -> [a] Hugs.Prelude> take 5 [0, 997 ..] -- like iota [0,997,1994,2991,3988] Hugs.Prelude> :t takeWhile takeWhile :: (a -> Bool) -> [a] -> [a] Hugs.Prelude> takeWhile (< 4000) [0, 997 ..] -- like range [0,997,1994,2991,3988]
そして、その実装。
take :: Int -> [a] -> [a] take n _ | n <= 0 = [] take _ [] = [] take n (x:xs) = x : take (n-1) xs takeWhile1 :: (a -> Bool) -> [a] -> [a] takeWhile1 p (x:xs) = x : if p x then takeWhile1 p xs else []
入力に相当する、(x:xs) の所で、既に、car,cdrに分解してある所が、非常な 狡獪さを感じる。後、右辺に出てくる、 ':' は、consに相当する。実に簡潔 だ。
同じことをpythonでやろうとすると、 Python のジェネレータ (4) - 無限リスト とか、 Pythonのitertools.count, cycle, repeatによる無限イテレータ のお世話になるのかな。
import itertools as it def take(n, iterable): return list(it.islice(iterable, n)) def takeWhile(pred, iterable): return list(it.takewhile(pred, iterable)) take(5, it.count(0, 997)) takeWhile(lambda x: x < 4000, it.count(0, 997))
一応実行結果をば
Python 3.11.2 (main, Feb 18 2023, 14:41:59) [GCC 12.2.1 20230201] on linux Type "help", "copyright", "credits" or "license" for more information. >>> [0, 997, 1994, 2991, 3988] >>> [0, 997, 1994, 2991, 3988]
gdb for python
前回の最後に、 Your Guide to the CPython Source Code なんてのに眼をつけておいた。じっ と読んでいくと、こんな風に、インストールせずに使った例がでてた。しかも、 shard-libにもしていないし。楽な方法をとっていた。
$ ./configure --with-pydebug $ make -j8 -s $ gdb ./python To enable execution of this file add add-auto-load-safe-path /home/sakae/Python-3.11.2/python-gdb.py (gdb) r Starting program: /home/sakae/Python-3.11.2/python [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1". Python 3.11.2 (main, Apr 14 2023, 05:49:22) [GCC 10.2.1 20210110] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Program received signal SIGINT, Interrupt. 0xb7fd2559 in __kernel_vsyscall () (gdb) ;; Ctl-x a (gdb) f 10
これでも、ちゃんとlibpythonの機能が利用できた。前回の苦労は何だったん だよう。おまけに、gdbのTUIモードも出てきてたし。。
思わず調べちゃったぞ。
instaviz
そして、ダラダラ先に進むと、コンパイルした結果の木をWebから観測できる 方法が、紹介されてた。これは、やってみる鹿。
$ pip install instaviz Requirement already satisfied: install in ./.local/lib/python3.9/site-packages (1.3.5) Collecting instaviz Downloading instaviz-0.6.0-py3-none-any.whl (462 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 462.4/462.4 kB 2.2 MB/s eta 0:00:00 Requirement already satisfied: pygments in ./.local/lib/python3.9/site-packages (from instaviz) (2.15.0) Collecting dill Downloading dill-0.3.6-py3-none-any.whl (110 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 110.5/110.5 kB 1.3 MB/s eta 0:00:00 Collecting jinja2 Using cached Jinja2-3.1.2-py3-none-any.whl (133 kB) Collecting bottle Downloading bottle-0.12.25-py3-none-any.whl (90 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 90.2/90.2 kB 1.3 MB/s eta 0:00:00 Collecting MarkupSafe>=2.0 Downloading MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (25 kB) Installing collected packages: bottle, MarkupSafe, dill, jinja2, instaviz Successfully installed MarkupSafe-2.1.2 bottle-0.12.25 dill-0.3.6 instaviz-0.6.0 jinja2-3.1.2
パイプのコードを木にしてみる。
>>> def pipe(data, *funcs): for f in funcs: data = f(data) return data >>> import instaviz >>> instaviz.show(pipe) Bottle v0.12.25 server starting up (using WSGIRefServer())... Listening on http://localhost:8080/ Hit Ctrl-C to quit.
んが、ローカルホストにアクセスしなさいって言われても、諸般の事情で困る のよ。こういう時は、Windowsに入れてるmingwじゃと思ったら、どうも役不足 というか、パッケージのエラーになった。深追いはヤメ。
ローカルホストでWebサーバーじゃなくて、与えられたIPでサーバーを起動し たい。変更可能か調査。 /site-packages/instaviz/web.py
def start(host="localhost", port=8080): """ Run the web server """ # set TEMPLATE_PATH to use an absolute path pointing to our directory abs_app_dir_path = os.path.dirname(os.path.realpath(__file__)) abs_views_path = os.path.join(abs_app_dir_path, "templates") TEMPLATE_PATH.insert(0, abs_views_path) run(host=host, port=port) print(f"Running web-server on http://{host}:{port}/")
もう、ここを変更するんですよって書いてあったぞ。何の苦労もなく、変更終 了。
そして、肝はこちら。
bottle.py/run
Bottle is a fast and simple micro-framework for small web applications. It offers request dispatching (Routes) with url parameter support, templates, a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and template engines - all in a single file and with no dependencies other than the Python Standard Library. Homepage and documentation: https://bottlepy.org/
ちょいと使うには便利っぽい。
Bottle(pythonフレームワーク)のチュートリアル日本語訳
127.0.0.1 - - [21/Apr/2023 06:46:37] "GET / HTTP/1.1" 200 26391 127.0.0.1 - - [21/Apr/2023 06:46:38] "GET /static/bootstrap.min.css HTTP/1.1" 200 155758 127.0.0.1 - - [21/Apr/2023 06:46:38] "GET /static/vis.js HTTP/1.1" 200 1854177 127.0.0.1 - - [21/Apr/2023 06:46:38] "GET /static/vis-network.min.css HTTP/1.1" 200 14426 127.0.0.1 - - [21/Apr/2023 06:46:38] "GET /static/json2html.css HTTP/1.1" 200 752 127.0.0.1 - - [21/Apr/2023 06:46:38] "GET /static/jquery.min.js HTTP/1.1" 200 97163 127.0.0.1 - - [21/Apr/2023 06:46:38] "GET /static/jquery.json2html.js HTTP/1.1" 200 4626 127.0.0.1 - - [21/Apr/2023 06:46:38] "GET /static/json2html.js HTTP/1.1" 200 13600 127.0.0.1 - - [21/Apr/2023 06:46:38] "GET /static/node.js HTTP/1.1" 200 2528 127.0.0.1 - - [21/Apr/2023 06:46:39] "GET /favicon.ico HTTP/1.1" 404 742
ログを見てもhtmlなファイルを見ても、オイラーが大嫌いなjavascriptしか出 てこない。これはもう、尻尾を巻いて逃げましょ。
ast
上記の元となる技術は、こちら
cat pipe.py def pipe(data, *funcs): for f in funcs: data = f(data) return data
毎度お馴染みの奴を実験台に据えてみる。
python3 -c 'import ast; print(ast.dump(ast.parse(open("pipe.py").read()), indent=4))' Module( body=[ FunctionDef( name='pipe', args=arguments( posonlyargs=[], args=[ arg(arg='data')], vararg=arg(arg='funcs'), kwonlyargs=[], kw_defaults=[], defaults=[]), body=[ For( target=Name(id='f', ctx=Store()), iter=Name(id='funcs', ctx=Load()), body=[ Assign( targets=[ Name(id='data', ctx=Store())], value=Call( func=Name(id='f', ctx=Load()), args=[ Name(id='data', ctx=Load())], keywords=[]))], orelse=[]), Return( value=Name(id='data', ctx=Load()))], decorator_list=[])], type_ignores=[])
dis
そして機械語を確認。
>>> import dis >>> dis.dis(pipe) 2 0 LOAD_FAST 1 (funcs) 2 GET_ITER >> 4 FOR_ITER 12 (to 18) 6 STORE_FAST 2 (f) 3 8 LOAD_FAST 2 (f) 10 LOAD_FAST 0 (data) 12 CALL_FUNCTION 1 14 STORE_FAST 0 (data) 16 JUMP_ABSOLUTE 4 4 >> 18 LOAD_FAST 0 (data) 20 RETURN_VALUE
これ以上深入りしても面白い事は無さそう。
haskell
突然、鞍替えしてみる。
関数プログラミング―質の高いコードをすばやく直感的に書ける! 記事一覧
ArchLinuxだと、stackから使うのが便利かな。
[sakae@arch tmp]$ stack repl Configuring GHCi with the following packages: GHCi, version 9.2.7: https://www.haskell.org/ghc/ :? for help Loaded GHCi configuration from /tmp/haskell-stack-ghci/2a3bbd58/ghci-script ghci> takeWhile (< 20) [1,3 ..] [1,3,5,7,9,11,13,15,17,19] ghci> take 5 [1,3 ..] [1,3,5,7,9]
ちょっとこない間に随分バージョンが進歩してるな。