2012年9月11日火曜日

vimのpython拡張のための依存管理

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

vimにはif_pyなるものがあって、vimをpythonからいじることができる。 詳細はvimで:help pythonとかしてみるとわかる。

pythonで作ったものを混ぜてリリースするとなると packageの管理が必要なのですが、ありがちなのはvirtualenvでの管理。

$ cd /home/nori/Desktop/work/vim/junk
$ virtualenv --clear --no-site-packages

とかしておいて、vimを起動。 次のようなvim scriptをquickrunで実行すると、

python << EOF
import vim
import sys
for p in sys.path:
    print p
EOF

次のような結果を得る。

/home/nori/Desktop/work/vim/junk/.python/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg
/home/nori/Desktop/work/vim/junk/.python/lib/python2.6/site-packages/pip-1.0.2-py2.6.egg
/home/nori/Desktop/work/vim/junk/.python/lib64/python26.zip
/home/nori/Desktop/work/vim/junk/.python/lib64/python2.6
/home/nori/Desktop/work/vim/junk/.python/lib64/python2.6/plat-linux2
/home/nori/Desktop/work/vim/junk/.python/lib64/python2.6/lib-tk
/home/nori/Desktop/work/vim/junk/.python/lib64/python2.6/lib-old
/home/nori/Desktop/work/vim/junk/.python/lib64/python2.6/lib-dynload
/usr/lib/python2.6
/usr/lib64/python2.6
/usr/lib64/python2.6/lib-tk
/home/nori/Desktop/work/vim/junk/.python/lib/python2.6/site-packages

これの意味するところは、virtualenvを実行してから vimを立ち上げれば、if_pyの開発での依存関係をvirtualenv/pipで管理できるということだ。

また、VimShellからipythonを立ち上げても当然virtualenvも同じになる。もちろんVimShellからのpip installも可能なので、vimに引きこもれます。

2012年8月22日水曜日

pyephem

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

pyephemを使って、hipparcosのdataをロードしようとしているのだが、角度関係が変。

ephem.degrees(x)のxにはradianを渡すらしい。

  ephem.degrees(degree2rad(entry["RAdeg"])),
  ephem.degrees(degree2rad(entry["DEdeg"])),
私の感覚だと、pythonでは普通、
def Angle(degree=None, hours=None, radian=None):
    ...
のようなインターフェースになると思うんだが・・・・・。(3つのうち同時に与えられるのは1つだけ)

in __init__.py of ephem

# We make available several basic types from _libastro.

Angle = _libastro.Angle
degrees = _libastro.degrees
hours = _libastro.hours

in astro.h

#define raddeg(x)   ((x)*180./PI)

in _libastro.c

static PyObject* build_degrees(double radians)
{
     return new_Angle(radians, raddeg(1));
}

static PyObject* build_degrees_from_degrees(double degrees)
{
     return build_degrees(degrees / raddeg(1));
}    

同じく _libastro.c

static PyObject* new_Angle(double radians, double factor)
{
     AngleObject *ea;
     ea = PyObject_NEW(AngleObject, &AngleType);
     if (ea) {
      ea->f.ob_fval = radians;
      ea->factor = factor;
     }
     return (PyObject*) ea;
}

こいつがephem.degrees()として公開されている。つーか、ea-<factorって何がやりたいの? radian固定ならfactorって何のためにあるのやら。

同じく _libastro.c

static PyObject *degrees(PyObject *self, PyObject *args)
{
     PyObject *o;
     double value;
     if (!PyArg_ParseTuple(args, "O:degrees", &o)) return 0;
     if (parse_angle(o, raddeg(1), &value) == -1) return 0;
     return new_Angle(value, raddeg(1));
}    

同じく _libastro.c

/*   
 * The global methods table and the module initialization function.
 */
     
static PyMethodDef libastro_methods[] = {
     
     {"degrees", degrees, METH_VARARGS, "build an angle measured in degrees"},
     {"hours", hours, METH_VARARGS, "build an angle measured in hours of arc"},
     {"now", (PyCFunction) build_now, METH_NOARGS, "Return the current time"},

2012年8月9日木曜日

Haskellことはじめ fizzbuzz

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
disられているらしいizzbuzzですが、どんな言語か知るために手をつけ始めるときにはよい課題かと思います。何事もそうですが目的にかなっているかどうかです。

izzbuzz :: Int -> String
fizzbuzz n | (n `mod` 15) == 0 = "fizzbuzz"
        | (n `mod` 3) == 0 = "fizz"
        | (n `mod` 5) == 0 = "buzz"
        | otherwise =  show n

main = do
  print $ map fizzbuzz [1,2 .. 20]
showによってprintの前に型の情報がつぶされているので気に入らなかった。で・・・・eitherとかmaybeってものがある。これをつかうとfizzbuzz関数が返す型が本質的に記述できる
fizzbuzz :: Int -> Either String Int
fizzbuzz n | (n `mod` 15) == 0 = Left "fizzbuzz"
        | (n `mod` 3) == 0 = Left "fizz"
        | (n `mod` 5) == 0 = Left "buzz"
        | otherwise = Right n

main = do
  print $ map fizzbuzz [1,2 .. 20]
ありがちな無限列を返すという形にしてみる。
fizzbuzz :: Int -> Either String Int
fizzbuzz n | (n `mod` 15) == 0 = Left "fizzbuzz"
        | (n `mod` 3) == 0 = Left "fizz"
        | (n `mod` 5) == 0 = Left "buzz"
        | otherwise = Right n

fizzbuzzStream :: [Either String Int]
fizzbuzzStream = map fizzbuzz [1,2 ..]

main = do
  print $ take 20 fizzbuzzStream
"/ \$"を消去とか清書。
fizzbuzz :: Int -> Either String Int
fizzbuzz n
  | n `mod` 15 == 0 = Left "fizzbuzz"
  | n `mod` 3 == 0 = Left "fizz"
  | n `mod` 5 == 0 = Left "buzz"
  | otherwise = Right n

fizzbuzzStream :: [Either String Int]
fizzbuzzStream = map fizzbuzz [1,2 ..]

main = do
  print $ take 20 fizzbuzzStream

2012年8月7日火曜日

JPLのコーディング規約を読む(part 1)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
コーディング規約のソースはこちら。 JPL Institutional Coding Standard for the C Programming Language, (PDF)
JPLといってもAPLの親戚言語かではなくて ジェット推進研究所(Jet Propulsion Laboratory)のほうのJPLです。 言い方を変えると、オポチュニティとスピリットを作ったところです。
探査機の制御とかが念頭にあると思われるので、web applicationやサーバ、GUIアプリケーションとは違った方針になっているでしょう。実>際、page 6のScopeで
The coding rules defined here primarily target the development of mission critical flight software written in the C programming language. This means that the rules are focused on embedded software applications, which generally operate under stricter resource constraints than, e.g., ground software.
となると、とにかくエラーがあることは話にならないし、仮にあとから修正することも非常に困難な訳です。 (このへんwebとか非常にだらしない・・・。宇宙機での変更は不可能ではないが宇宙の彼方にプログラムを転送して(すごく遅い!)、再起動という>恐ろしいことになります。) 打ち上げや、軌道変更、誘導、着陸でミスをすれば膨大な開発期間とコストが一瞬でパーになりプロジェクトが失敗に終わるわけです。 太陽電池が展開できないとかなら電力が無くて死亡、アンテナの向きがおかしいとかでも地球側と通信できなくなってやはりダメでしょう。

RFCとかその他いろいろな仕様書関連である、Shallとかmustとかの意味が一般英語と異なるので、ちゃんと定義があります。

  • Shall indicates a requirement that must be followed, with compliance verified.
  • Should indicates a preference that must be addressed, but with deviations allowed, provided that an adequate justification is given for each deviation

shallは例外のない絶対で、shouldはそうされることがきわめて好ましいが十分な正当化があれば行ってもよい、ということです。

これで最低限の準備ができたので、Page 4のsummaryにある31箇条+2をみていきましょうか。いきなり欄の一番下です。

*) All rules are shall rules, except those marked with an asterix.

つまり、アスタリスクが付いていないやつは絶対まもれ、といっているのです。

それでは1-19の適当訳とコメント。のこりはpart2かな。

  1. Language Compliance 「言語の仕様に従え」
    1. Do not stray outside the language definition

      変態的なマクロとかを書いて拡張っぽいことをするとかを指しているのかな?本体を読めば詳細がわかるのでしょう。

    2. Compile with all warnings enabled; use static source code analyzers.

      gccだったら-Wallかな。clangにもなにかあるはず。今時lintでも無いと思うけど、代わりになる道具はありそうだ。

  2. Predictable Execution 「予測・予期可能な実行」

    1. Use verifiable loop bounds for all loops meant to be terminating.

      脱出する意図のあるループ構造には検証可能な脱出条件を使いなさい。

    2. Do not use direct or indirect recursion.

      再帰呼び出しを直接間接を問わず使うべからず。

      理由は簡単に予想が付く。スタックがオーバーフローする可能性があるからだ。 リソースが制限されている組み込みのジャンルではおそらく普通だろう。

    3. Do not use dynamic memory allocation after task initialization

      タスクの初期化後にメモリを動的に確保するな

      これはちょっとよくわからない。予想不能な量のメモリを消費するなということだろうか?

    4. *Use IPC messages for task communication.

      IPCメッセージはタスク間通信に使え。

      よくわからず。ところでOSは何を使っているのだろうねぇ。

    5. Do not use task delays for task synchronization.

      task遅延をタスク同期に用いるな。

      taskの定義が曖昧なので前者とともに謎

    6. *Explicitly transfer write-permission (ownership) for shared data objects.

      共有されているオブジェクトの書き込み権限(所有権)は明示的に引き渡せ

      おそらくは「タスク間で」だと思われる。それほどの驚きはない。 最近流行の変更不能なvalueだけでコードを書くようなスタイルは 予想不能な量のメモリを使用するため採用できない。従って昔ながら(?)の lockを使ったプログラミングにならざるを得ない。当然lockの取得がらみで問題が起こるので、 このような規約を採用することになるのだろう。 *つきなのは当然で、どのようにshareされるかは解きたい問題とそれの解決手法次第だから。

    7. Place restrictions on the use of semaphores and locks.

      セマフォとロックの使い方に関して制限を設けよ

      dead lockは困りますからねぇ・・・

    8. Use memory protection, safety margins, barrier patterns.

      メモリ保護、安全マージン、バリアパターンを使え。

      メモリ保護はOSの機能かな?これは驚かない。 安全マージンとバリアパターンが具体的に何を意味しているかがわからない。 読めば出てくるのかもしれない。予想としては配列の前後に0xdeadbeafとかを埋め込むってことかな?

    9. Do not use goto, setjmp or longjmp.

      setjump, longjump, goto禁止

      とくに驚かない。構造化プログラムってことだね。 Duff's device みたいな変態は許されるのだろうかねぇ・・・。

    10. Do not use selective value assignments to elements of an enum list.

      enumリストで選択的に値を代入するな。

      おそらく次のようなコードを意味するのだと思う。

      enum Kitty { MIMI , YUKI = 5 , RENA };

      これをやると値が非連続になり、バグの元になるのだろう。

  3. Defensive Coding 「防御的プログラミング」

    [プログラミング原則][防御的プログラミング]防御的プログラミングとかが参考になるのでは。

    1. Declare data objects at smallest possible level of scope.

      可能な限り最小のスコープになるようにオブジェクトを宣言せよ。

      lisp系のletとかでもそうですね。そこそこ書いたことがあれば守っているはずの基本原則でな。

    2. Check the return value of non-void functions, or explicitly cast to (void).

      関数の戻り値はチェックしなさい。void関数なら明示的にvoidにキャストしなさい。

      キャストせよ、というのは新鮮。ほぼvoidな関数を使うなに同義ではないだろうか。 そのようなキャストを行うコードは変に見えるだろう。発想として、 間違ったコードは間違って 見えるようにする に似ているように思える。24にも関係する。

    3. Check the validity of values passed to functions.

      関数に渡された値を検証せよ

      契約・表明プログラミングの流れをくみますねぇ。おそらく。

    4. Use static and dynamic assertions as sanity checks.

      静的・動的表明を健全性検証に使え。

    5. * Use U32, I16, etc instead of predefined C data types such as int, short, etc.

      U32やI16を使い、Cであらかじめ規定されているint, shortなどの型は使うな。

      組み込み的。コンパイラによって何に変換されるか不明な型は使わない、ということだろう。

    6. Make the order of evaluation in compound expressions explicit.

      複合式の評価順序を明示せよ

      1 + 2 * 5とは書けないのかな?

    7. Do not use expressions with side effects

      副作用のある式を使うな。

      ということは、while(x =read())みたいなことはできんのかな?

2012年7月26日木曜日

pycをみる

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
ここを読んだときのメモ。 struct.unpackに渡すフォーマットがLじゃなくてiだってだけの話。おいらの環境が64bitだからかな。

マジック + CRLF + datetime(epock) + marshalled object

である。pickleじゃなくてmarshallな理由はこの辺:

The entire rest of the file is just the output of marshal.dump of the code object that results from compiling the source file. Marshal is like pickle, in that it serializes Python objects. It has different goals than pickle, though. Where pickle is meant to produce version-independent serialization suitable for persistence, marshal is meant for short-lived serialized objects, so its representation can change with each Python version. Also, pickle is designed to work properly for user-defined types, while marshal handles the complexities of Python internal types. The one we care about in particular here is the code object.

import dis, marshal, struct, sys, time, types
    
MAGICLENGTH = 4
DATELENGTH = 4
        
def show_file(fname):
    f = open(fname, "rb")
    magic = f.read(MAGICLENGTH)
    moddate = f.read(DATELENGTH)
    modtime = time.asctime(time.localtime(struct.unpack('i', moddate)[0]))
    print "magic %s" % (magic.encode('hex'))
    print "moddate %s (%s)" % (moddate.encode('hex'), modtime)
    code = marshal.load(f)
    show_code(code)
        
def show_code(code, indent=''):
    print "%scode" % indent
    indent += '   '
    print "%sargcount %d" % (indent, code.co_argcount)
    print "%snlocals %d" % (indent, code.co_nlocals)
    print "%sstacksize %d" % (indent, code.co_stacksize)
    print "%sflags %04x" % (indent, code.co_flags)
    show_hex("code", code.co_code, indent=indent)
    dis.disassemble(code)
    print "%sconsts" % indent
    for const in code.co_consts:
        if type(const) == types.CodeType:
            show_code(const, indent+'   ')
        else:
            print "   %s%r" % (indent, const)
    print "%snames %r" % (indent, code.co_names)
    print "%svarnames %r" % (indent, code.co_varnames)
    print "%sfreevars %r" % (indent, code.co_freevars)
    print "%scellvars %r" % (indent, code.co_cellvars)
    print "%sfilename %r" % (indent, code.co_filename)
    print "%sname %r" % (indent, code.co_name)
    print "%sfirstlineno %d" % (indent, code.co_firstlineno)
    show_hex("lnotab", code.co_lnotab, indent=indent)
    
def show_hex(label, h, indent): 
    h = h.encode('hex')
    if len(h) < 60:
        print "%s%s %s" % (indent, label, h)
    else:
        print "%s%s" % (indent, label)
        for i in range(0, len(h), 60):
            print "%s   %s" % (indent, h[i:i+60])
        
show_file(sys.argv[1])

2012年6月23日土曜日

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
tumblrからLingrに流すコードを実装しているのだが、某所にあったコードをjsonに対応させて、黒魔術を施してみた。これはもはや原型をとどめていないだろう・・・。
#!/usr/bin/python
# -*- coding: utf-8 -*-

import httplib
from urllib import urlencode
import json
    
''' 
    ToDo(?)
    support message removal.
    there is no offical api for it.
    #http://lingr.com/room/computer_science/archives/2012/06-23#message-10303741
'''

HOST = "lingr.com"

class Session:
    api_key = None

    #https://github.com/lingr/lingr/wiki/Lingr-API
    mappings = {
            "api_session_create":"/api/session/create",
            "api_session_verify":"/api/session/verify",
            "api_session_destroy":"/api/session/destroy",
            "api_room_show":"/api/room/show",
            "api_room_get_archives":"/api/room/get_archives",
            "api_room_subscribe":"/api/room/subscribe",
            "api_room_unsubscribe":"/api/room/unsubscribe",
            "api_room_say":"/api/room/say",
            "api_user_get_rooms":"/api/user/get_rooms"}

    def __init__(self, user, password, nickname=None):
        '''
            Lingr ignores nickname!
        '''
        assert self.api_key
        self.conn = None
        self.user = user
        self.password = password

        self.values = {}
        if nickname:
            self.values["nickname"] = nickname
        else:
            self.values["nickname"] = user
        self.values['api_key'] = self.api_key

    def param(self, kw):
        d = {}
        d.update(self.values)
        d.update(kw)
        return d

    def __getattr__(self, name):
        assert name in self.mappings
        def handler(**args):
            self.conn.request('POST', self.mappings[name], urlencode(self.param(args)))
            res = self.conn.getresponse()
            d = res.read()
            j = json.loads(d)
            if j['status'] != 'ok':
                raise Exception(j)
            return j
        return handler
            
    def connect(self):
        self.conn = httplib.HTTPConnection(HOST)
        j = self.api_session_create(user=self.user, password=self.password)
        self.values['session']=j['session']
        return self
            
    def disconnect(self):
        self.conn.close() #?!

    def __enter__(self):
        return self.connect()

    def __exit__(self, exc_type, exc_value, traceback):
        return self.disconnect()
        
    def enter(self, room):
        self.values['room'] = room

    def say(self, text):
        return self.api_room_say(text=text)
            
        
if __name__ == '__main__':
    import sys
    if len(sys.argv) < 3:
        sys.exit()
    Session.api_key="kzRHJn"
    with Session(sys.argv[1], sys.argv[2]) as s:
        s.enter("computer_science")
        s.say("test, by lingr2.py in python.")

2012年5月10日木曜日

unlambda in C

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
unlambdaの実装をCで書いているのだが、sをちゃんとつかうプログラムでは、objectの寿命がかなりわかりにくい。ので。GCでもすんべ、と思った。よって、valgrind投入でございます。
作業補助のやる気ないMakefile
unlambda: unlambda.c
  gcc -Wall -g -o unlambda unlambda.c

test: unlambda
  valgrind -v --error-limit=no --leak-check=yes --show-reachable=no ./unlambda 2>&1 | tee valgrind.log

DeleteObjectを削除してmakeして得た結果。ばっちり検出してくれている。
==16830== Memcheck, a memory error detector
==16830== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==16830== Using Valgrind-3.6.0 and LibVEX; rerun with -h for copyright info
==16830== Command: ./unlambda
==16830==
--16830-- Valgrind options:
--16830--    -v
--16830--    --error-limit=no
--16830--    --leak-check=yes
--16830--    --show-reachable=no
--16830-- Contents of /proc/version:
--16830--   Linux version 2.6.32-220.4.2.el6.x86_64 (mockbuild@sl6.fnal.gov) (gcc version 4.4.6 20110731 (Red Hat 4.4.6-3) (GCC) ) #1 SMP Mon Feb 13 17:24:24 CST 2012
--16830-- Arch and hwcaps: AMD64, amd64-sse3-cx16
--16830-- Page sizes: currently 4096, max supported 4096
--16830-- Valgrind library directory: /usr/lib64/valgrind
--16830-- Reading syms from /home/nori/Desktop/study/c/unlambda/unlambda (0x400000)
--16830-- Reading syms from /usr/lib64/valgrind/memcheck-amd64-linux (0x38000000)
--16830--    object doesn't have a dynamic symbol table
--16830-- Reading syms from /lib64/ld-2.12.so (0x3135a00000)
--16830-- Reading suppressions file: /usr/lib64/valgrind/default.supp
--16830-- REDIR: 0x3135a17460 (strlen) redirected to 0x38042ae7 (vgPlain_amd64_linux_REDIR_FOR_strlen)
--16830-- Reading syms from /usr/lib64/valgrind/vgpreload_core-amd64-linux.so (0x4801000)
--16830-- Reading syms from /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so (0x4a02000)
==16830== WARNING: new redirection conflicts with existing -- ignoring it
--16830--     new: 0x3135a17460 (strlen              ) R-> 0x04a07830 strlen
--16830-- REDIR: 0x3135a172d0 (index) redirected to 0x4a07470 (index)
--16830-- REDIR: 0x3135a17350 (strcmp) redirected to 0x4a07df0 (strcmp)
--16830-- Reading syms from /lib64/libc-2.12.so (0x3135e00000)
--16830-- REDIR: 0x3135e83980 (strcasecmp) redirected to 0x4801560 (_vgnU_ifunc_wrapper)
--16830-- REDIR: 0x3135e85c40 (strncasecmp) redirected to 0x4801560 (_vgnU_ifunc_wrapper)
--16830-- REDIR: 0x3135e818f0 (__GI_strrchr) redirected to 0x4a072f0 (__GI_strrchr)
--16830-- REDIR: 0x3135e79760 (malloc) redirected to 0x4a05f10 (malloc)
Hello world
--16830-- REDIR: 0x3135e7a590 (free) redirected to 0x4a05890 (free)
==16830== 
==16830== HEAP SUMMARY:
==16830==     in use at exit: 1,000 bytes in 25 blocks
==16830==   total heap usage: 25 allocs, 0 frees, 1,000 bytes allocated
==16830== 
==16830== Searching for pointers to 25 not-freed blocks
==16830== Checked 65,664 bytes
==16830== 
==16830== 960 (440 direct, 520 indirect) bytes in 11 blocks are definitely lost in loss record 4 of 4
==16830==    at 0x4A05FDE: malloc (vg_replace_malloc.c:236)
==16830==    by 0x40058F: NewObject (unlambda.c:33)
==16830==    by 0x400701: print (unlambda.c:103)
==16830==    by 0x40098F: eval (unlambda.c:213)
==16830==    by 0x400A2D: main (unlambda.c:247)
==16830== 
==16830== LEAK SUMMARY: 
==16830==    definitely lost: 440 bytes in 11 blocks
==16830==    indirectly lost: 520 bytes in 13 blocks
==16830==      possibly lost: 0 bytes in 0 blocks
==16830==    still reachable: 40 bytes in 1 blocks
==16830==         suppressed: 0 bytes in 0 blocks
==16830== Reachable blocks (those to which a pointer was found) are not shown.
==16830== To see them, rerun with: --leak-check=full --show-reachable=yes
==16830==    
==16830== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 6)
--16830--   
--16830-- used_suppression:      6 dl-hack3-cond-1
==16830== 
==16830== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 6)

2012年5月8日火曜日

方針の良し悪し・・・小さな1つの経験

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
一般的に方針の良し悪し、というと語ることは不可能なのだが、bfの処理系で得た知見をあげるなら、同じ仕様のものを作っていても、 「テストのカバレッジが異なる実装が存在しうる、ということ。

どういうことかというと:次の2つの実装をテストとしたときに

前者は多重入れ子があるときに最初問題を起こした。後者は入れ子でテストさえすれば、多重でも大丈夫なコトが分かる。

言語の処理系を利用している、と言う点でevalが有利なのは当たり前なのだが、この件はそれとはまた別の要素ではないだろうか。

残念ながら私の今までの経験ではこれをもっと一般的に語ることは不可能だ。

最近楽しんでいるもの

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

最近いじって楽しんでいるものはcloftです。

push権限ももらっていい気になっています。

cloftはclojureでかかれたbukkit pluginで、bukkitとは、minecraftのサーバです。

まだまだよくわからないことの多いclojureですが、lisp-1で関数と「変数」(正しくない書き方だろう)の名前空間が一緒なのが胡のみですね。

またカッコを減らそうという努力が伺えるのが好感度高いです。(じゃあHaskellやれよ、という声もあるのかもしれませんが・・・・)

「clojure では、マップ型および、キーワード型は、callable」で、なるほど。よく考えてある。pythonとかでやらかう[]と()をタイポしてむかつくことがないのかというのもいいです。

#!Clojure
({:a 10 :b 20 :c 30} :b)
=> 20

いままでイラつきを引き起こしている言語のくだらん記法を減らす設計に向かっているということが大事。

わかっている人に言わせればrecurとかも実用的判断らしいですが、まだよくわかりません。

専門家と議論するより、小中学生の集団を相手に会話する方が語学的にははるかに難しい。

コンテキストとスコープが共有されないと大変。 またどの範囲で効いているのかが重要で、取り違えるとまっとうな意味をなさない。

人間のコミュニケーションも、言語も一緒だよね。コンテキストを作り出す コストが高い言語は何をしても大変だろう。

話はcloftでのコードに戻る。

先に断っておくと、minecraftをやったことがないと分かりにくいと思う。

multithreadをつかってbukkitとやりとりしたらなんかおかしいのでそれを止めた

具体的にはfuture-callを使ってblockを積んだらおかしくなったのだ。

仕方ないのでJavaScriptのtimerっぽいものを実装した

大筋では

  • 起動されてからtickごとに毎回処理するcloft-scheduler
  • 何回目のtickだったか覚えるcurrent-tick
  • callbackとその発火時刻を覚えておくschedule-table
  • 「ユーザ」がcallbackの登録と発火時刻の指定に使うsettimer
からできている。

最初tableが要素にもつvectorはmutableである必要がないことに気づかなくてハマった

vectorをconsするなという噂もあります。

(def cloft-schedule-table (atom {}))
(def cloft-schedule-currenct-tick (atom 0))
(defn cloft-schedule-settimer [after f]
  ;(prn cloft-schedule-settimer after f)
  ;(prn (count @cloft-schedule-table))
  (dosync
   (let [wake-up (+ @cloft-schedule-currenct-tick after)]
     (swap! cloft-schedule-table assoc wake-up
            (cons f (@cloft-schedule-table wake-up []))))))

(defn cloft-scheduler []
  (dosync
    (let [table @cloft-schedule-table
          now @cloft-schedule-currenct-tick
          r (table now false)]
      (when r
        ;(prn r table now)
        (doseq [f r] (f)))
      (swap! cloft-schedule-table dissoc @cloft-schedule-currenct-tick))
    (swap! cloft-schedule-currenct-tick inc)))

これをplugin起動時に登録する。サーバ側は1tickごとにcloft-schedulerを呼び出す。ちなみに1tickは50msです。

  (.scheduleSyncRepeatingTask (Bukkit/getScheduler) plugin (fn [] (cloft-scheduler)) 0 1)

2012年5月4日金曜日

bf処理系、Cで構文木(?)作るバージョン

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

bf2c.pyでトランスレータはおもしろくないことが分かっていたので、解析してtreeを作らせた。ほぼlispのcons対。

include 
#include 
#include 


/*
 *  http://en.wikipedia.org/wiki/Brainfuck
 */

char* helloworld_expected = "Hello World!\n";
char* helloworld = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.";


/* 
 *  http://codegolf.com/competition/run/8/2
 */

char* another_expected = "Just another brainfuck hacker.";
char* another = 
  "+++[>+++++<-]>[>+>+++>+>++>+++++>++<[++<]>---]>->-.[>++>+<<--]>--.--.+.>>>++.<<"
  ".<------.+.+++++.>>-.<++++.<--.>>>.<<---.<.-->-.>+.[+++++.---<]>>[.--->]<<.<+.+"
  "+.++>+++[.<][.]<++.";


char memory[30000];
char *data = memory;
char *tape= 0;

typedef struct TAst Ast;

union UValue {
  char uChar;
  Ast *uAst;
};

typedef union UValue Value;

struct TAst {
  Ast* fNext;
  int fType;
  Value fValue;
};


int debug = 0;

Ast* make_node(Ast* parent){
  Ast* ast;
  ast = malloc(sizeof(Ast));
  if(parent){
    parent->fNext = ast;
  }

  if(debug)
    printf("makde ast node %p (parent %p)\n", ast, parent);//->fValue.uAst);
  return ast;
}

Ast* parse(char** p){
  Ast* root;
  Ast* head;
  Ast* n;
  char* sub;

  root = make_node(0);
  root->fNext = 0;
  root->fType = 0;
  head = root;

  while (**p){
    switch(**p){
      case '[':
        n = make_node(head);
        (*p)++; //consume [
        n->fNext = 0;
        n->fType = 0;
        n->fValue.uAst = parse(p);
        head = n;
        break;
      case ']': 
        // ] will be consumed after return, in '[' case.
        return root->fNext;
      default:
        n = make_node(head);
        n->fNext = 0;
        n->fType = 1;
        n->fValue.uChar = **p;
        head = n;
        break;
    }
    (*p)++;
  }
  return root->fNext;
}

void print_ast(Ast *ast, int indent){
  int i;
  
  while(ast){
    if (ast->fType){
      for(i=0; i< indent; i++){
        printf(" ");
      }
      printf("%c\n", ast->fValue.uChar);
    }else{
      for(i=0; i< indent; i++){
        printf(" ");
      }
      printf("Entered %p \n", ast->fValue.uAst);
      print_ast(ast->fValue.uAst, indent+2);
    }
    ast = ast->fNext;
  }
}


void run(Ast *ast){
  while (ast){
    if(debug)
      printf("%i, %i, %i, %i, %i\n", memory[0], memory[1], memory[2], memory[3], memory[4]);
    if (ast->fType){
      if(debug)
        printf("processing %c\n", ast->fValue.uChar);
      switch (ast->fValue.uChar){
        case '>':
          /* increment the data pointer (to point to the next cell to the right). */
          data++;
          break;
    
        case '<':
          /* decrement the data pointer (to point to the next cell to the left). */
          data--;
          break;
    
        case '+':
          /* increment (increase by one) the byte at the data pointer. */
          (*data)+=1;
          break;
    
        case '-':
          /* decrement (decrease by one) the byte at the data pointer. */
          (*data)-=1;
          break;
          
        case '.':
          /* output the byte at the data pointer as an ASCII encoded character.*/
          if(debug)
            printf("%i", *data);
          printf("%c", *data);
          break;
    
        case ',':
          /* accept one byte of input, storing its value in the byte at the data pointer. */
          /* not implemented */
          break;
        default:
          break;
      }
    }else{
      if(debug)
        printf("handling subtree, %p\n", ast->fValue.uAst);
      /* subtree from []*/
      /* If the byte at the data pointer is zero, jump it forward to the command after the matching ] command*. */
      /* if the byte at the data pointer is nonzero, jump it back to the command after the matching [ command*. */
      while(*data){
        run(ast->fValue.uAst);
      }
    }
    ast = ast->fNext;
  };
}


int main(arg, argv){
  Ast* ast;
  char* tape;
  //tape = helloworld;
  tape = another;
  ast = parse(&tape);
  if(debug){
    printf("============================\n");
    print_ast(ast,0);
    printf("============================\n");
  }

  run(ast);
  return 0;
}
     

bf処理系、インタプリタなC言語バージョン

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

時系列的にはpythonのあと、rubyの前に書いている

include 
#include 
#include 


/*
 *  http://en.wikipedia.org/wiki/Brainfuck
 */

char* helloworld = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.";
char* helloworld_expected = "Hello World!\n";


/* 
 *  http://codegolf.com/competition/run/8/2
 */

char* another = 
  "+++[>+++++<-]>[>+>+++>+>++>+++++>++<[++<]>---]>->-.[>++>+<<--]>--.--.+.>>>++.<<"
  ".<------.+.+++++.>>-.<++++.<--.>>>.<<---.<.-->-.>+.[+++++.---<]>>[.--->]<<.<+.+"
  "+.++>+++[.<][.]<++.";
char* another_expected = "Just another brainfuck hacker.";


char memory[30000];
char *data = memory;
char *tape= 0;

char* bf(char* p, int run){
  char* r;
  char* start;

  //printf("%p, %i\n", p, run);

  while (*p){
    //printf("%i, %i, %i, %i, %i\n", memory[0], memory[1], memory[2], memory[3], memory[4]);
    //printf("%s\n", p);
    switch (*p){
      case '>':
        /* increment the data pointer (to point to the next cell to the right). */
        if(run){
          data++;
        }
        break;

      case '<':
        /* decrement the data pointer (to point to the next cell to the left). */
        if(run){
          data--; }
        break;

      case '+':
        /* increment (increase by one) the byte at the data pointer. */
        if(run){
          (*data)+=1;
        }
        break;

      case '-':
        /* decrement (decrease by one) the byte at the data pointer. */
        if(run){
          (*data)-=1;
        }
        break;
        
      case '.':
        /* output the byte at the data pointer as an ASCII encoded character.*/
        if(run){
          printf("%c", *data);
        }
        break;

      case ',':
        /* accept one byte of input, storing its value in the byte at the data pointer. */
        /* not implemented */
        if(run){
        }
        break;

      case '[':
        /* if the byte at the data pointer is zero, jump it forward to the command after the matching ] command*. */
        if (*data && run){
          start = p;
          assert(*start=='[');
          r = bf(p+1, 1);
          if(!r){
            r = start - 1; /* rewind, ajust against p++ */
          }
        }else{
          r = bf(p+1, 0); /* skip to ] */
          //printf("skipped to '%s'\n", r);
        }
        p = r;
        break;

      case ']':
        /* if the byte at the data pointer is nonzero, jump it back to the command after the matching [ command*.*/
        /* ==> */
        /* if the byte at the data pointer is zero, moving the instruction pointer forward */
        /* ==> */
        /* just rewind */
        if(run){
          return (char*)0;
        }else{
          return p;
        }
      default:
        break;
    };
    p++;
  };
  return (char*)0;
}


int main(arg, argv){
  //bf(helloworld, 1);
  bf(another, 1);
  return 0;
}
    

bf2c.py

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

これはpythonで書いたbfからCへのトランスレータ。wikipediaの解説、そのまま。

import sys

fname = sys.argv[1]

f = open(fname)
source = f.read()
f.close()

header = """
#include

int main(int argc, char**argv){
  char array[30000];
  char *ptr=array;

"""

footer = """
  return 0;
}
"""
mapping = {
  '>':  "++ptr;",
  '<':  "--ptr;",
  '+':  "++*ptr;",
  '-':  "--*ptr;",
  '.':  "putchar(*ptr);",
  ',':  "*ptr=getchar();",
  '[':  "while (*ptr) {",
  ']':  "}",
}

outname = fname[:-2]+'c'

f = open(outname, 'w')
f.write(header)

for c in source:
  o = mapping.get(c, '');
  f.write(o +'\n')

f.write(footer)
f.close()
    

bf処理系、pythonでevalバージョン

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
標記のバージョン。だいぶさっぱりしている。bfからpythonへのトランスレータなんですよね。 どの言語で書いてもトランスレータであるほうが楽。もっともbfからCとかだとgccが必要ですが。
import sys


source = sys.stdin.read()  

data = [0 for i in range(30000)]
L = {     
  "data" : data,
  "stdout" : sys.stdout,
  "idx" :0,
}

G ={}

mapping = {
  '>':  "idx+=1",
  '<':  "idx-=1",
  '+':  "data[idx]+=1",
  '-':  "data[idx]-=1",
  '.':  "stdout.write(chr(data[idx]))",
  ',':  " #not implemented ",
  '[':  "while data[idx]:",
  ']':  "False",
}


py = []
indent = ''
for c in source:
  o = mapping.get(c, '');
  py.append(indent + o + ' # ' + c +'\n')
  if c=='[':
    indent += '  '
  elif c == ']':
    indent = indent[:-2]
  else:
    pass
  #if c=='[':
  #  py.append(indent + 'print idx' + '\n')

p = ''.join(py)

#print len(data)
#for line, k in enumerate(p.split('\n')):
#  print '%4d: %s'%(line, k)
exec p in G, L
    

bf処理系 (python non-eval)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
んで、文字列のリスト化を踏まえてbrainf**kの処理系をかけとのお題が...

pythonを用いたnon-eval version。結構苦戦した。「ブロック」の扱いを正しく理解していなかった。

from sys import stdout


debug = True
class BF:
  '''
  see http://en.wikipedia.org/wiki/Brainfuck
  >>> tape = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>."
  >>> bf = BF(tape)
  >>> bf.run()
  Hello,World!

  >>> 
  '''
  ops = {
    '>': 'incp',
    '<': 'decp',
    '+': 'incd',
    '-': 'decd',
    '.': 'putc',
    ',': 'readc',
    '[': 'blockstart',
    ']': 'blockend',
  }
  
  def __init__(self, xs, data=None, ptr=None, dryrun=None, parent=None):
    self.parent = parent
    self.terminate = False
    self.orig = str(xs)
    self.xs = str(xs)
    if dryrun is None or not dryrun :
      self.dryrun=False
    else:
      self.dryrun=True
    if debug:
      print self, self.parent, self.dryrun

    if ptr is None:
      self.ptr = 0
    else:
      self.ptr = ptr
    if data is None:
      # assuming ptr is of type unsigned char* and has been initialized to point
      # to an array of zeroed bytes:
      self.data = [0 for i in range(30000)]
    else:
      self.data = data
    self.sub = None

  def run(self):
    while self.xs and not self.terminate:
      x = self.xs[0]
      self.xs = self.xs[1:]
      if debug:
        print self.ptr, self.data[:5], self.xs
        print x, self.dryrun, self.parent
      name = self.ops[x]
      if self.dryrun:
        if name not in ("blockstart",  "blockend"):
          name = 'null'
      handler = getattr(self, 'handle_' + name )
      handler() 
      
  def handle_null(self):
    pass
    
  def handle_incp(self):
    '>'
    self.ptr += 1
    
  def handle_decp(self):
    '<'
    self.ptr -= 1
    
  def handle_incd(self):
    '+'
    self.data[self.ptr] += 1
    
  def handle_decd(self):
    '-'
    self.data[self.ptr] -= 1
    
  def handle_putc(self):
    '.'
    #print self.data[self.ptr]
    stdout.write(chr(self.data[self.ptr]))
    stdout.flush()
    
  def handle_readc(self):
    ','
    # not used in Hello,World!
    pass
    
  def handle_blockstart(self):
    '['
    '''
      if the byte at the data pointer is zero, then instead of moving the
      instruction pointer forward to the next command, jump it forward to the
      command after the matching ] command*.
    '''
    if self.data[self.ptr] and not self.dryrun:
      # go into block
      dryrun = False 
    else:
      # skip until matching ']'
      dryrun = True 

    self.sub = BF(self.xs, self.data, self.ptr, dryrun, self)
    self.sub.run()
    if debug:
      print 'leaving', self.sub
   
    #write back child state
    self.data = self.sub.data #not needed. 
    self.ptr = self.sub.ptr
    self.xs = self.sub.xs

  def handle_blockend(self):
    '''
      if the byte at the data pointer is nonzero, then instead of moving the
      instruction pointer forward to the next command, jump it back to the command
      after the matching [ command*.
    '''
    ']'
    if self.data[self.ptr] and not self.dryrun:
      #jump back by rewind
      self.xs = '[' + self.orig 
    else:
      #step out 
      pass
    self.terminate = True


if __name__ == "__main__":
  #import doctest
  #doctest.testmod()
  #tape = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>."
  #tape = "[][]" 
  tape ='''+++[>+++++<-]>[>+>+++>+>++>+++++>++<[++<]>---]>->-.[>++>+<<--]>--.--.+.>>>++.<<'''\
      + '''.<------.+.+++++.>>-.<++++.<--.>>>.<<---.<.-->-.>+.[+++++.---<]>>[.--->]<<.<+.+'''\
      + '''+.++>+++[.<][.]<++.'''
  #tape = "[[]]" 
  print tape
  debug =False


  bf = BF(tape)
  bf.run()
    

文字列のリスト化、ruby編

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
ruby編。pythonよりスパッと解けているのは問題をより把握したため。

eval使うバージョン

s = "ab[cd]ef"
s = "ab[c[d]][e]f"
#expected = ['a', 'b', ['c', 'd' ], 'e', 'f']


s = s.gsub(/([\]a-z])/, '\1,')
s = s.gsub(/([a-z])/, '"\1"')
s = s.gsub(/,\]/, ']')
#puts s


p eval('[' + s +']')

    

eval使わないバージョン

s = "ab[cd]ef"
expected = ['a', 'b', ['c', 'd' ], 'e', 'f']




def parse(xs)
  result = []

  while not xs.empty?
    x = xs[0]
    xs.slice!(0, 1)
    if x == '['
      got = parse(xs)
      result.push(got)
    elsif x == ']'
      return result
    else
      result.push(x)
    end
  end
  return result
end


p parse("[][]")
p parse(s)

    

文字列のリスト化回答編(python)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

非evalバージョン

class Hoge:
  def __init__(self, xs):                                        
    self.xs = xs                                                 
    self.result = []                                             
    self.sub = None                                              
  
  def run(self):                                                 
    if not self.xs: 
      return self.result
    while self.xs:
      x = self.xs[0]
      self.xs = self.xs[1:]
      if x == '(':                                               
        self.sub = Hoge(self.xs)                                 
        self.sub.run()                                           
        self.result.append(self.sub.result)                      
        self.xs = self.sub.xs                                    
      elif x == ')':                                             
        return                                                   
      else:
        self.result.append(x)                                    
  
def f(x):                                                        
  h = Hoge(x)                                                    
  h.run()                                                        
  return h.result                                                
  
                                                                 
def test0():                                                     
  return f('') == []
                                                                 
def test1():                                                     
  return f('a') == ['a']                                         
                                                                 
def test2():                                                     
  return f('()') == [[]]
                                                                 
def test3():                                                     
  return f('(()())') == [[[],[]]]
                                                                 
def testX():                                                     
  return f("ab(cd)ef") == ['a', 'b', ['c', 'd'], 'e', 'f']       

print test0()                                                    
print test1()                                                    
print test2()
print test3()
print testX()
    

evalバージョン

import re

r = re.compile('a-zA-Z')

def f(xs):
  #'a' => 'a,'
  #'(' => '['
  #')' => ']'
  p = xs
  p = re.sub(r'\(', r"[", p)
  p = re.sub(r'\)', r"]", p)
  p = re.sub(r'([a-zA-Z])', r"'\1',", p)
  p = re.sub(r"\]'([a-zA-Z])", r"], '\1", p)
  p = re.sub(r'\]\[', r"],[", p)
  
  if not p:
    p = ' '
  p = '[' + p + ']'
  print p
  x = eval(p)
  print x
  y = list(x)
  print y
  return y



def test0():
  return f('') == []

def test1():
  return f('a') == ['a']

def test2():
  return f('()') == [[]]

def test3():
  return f('(()())') == [[[],[]]]

def testX():
  return f("ab(cd)ef") == ['a', 'b', ['c', 'd'], 'e', 'f']


print test0()
print test1()
print test2()
print test3()
print testX()
    
このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

文字列を回答に使用する言語のリストに変換せよ

ただしカッコがマッチしているコトを仮定してよい

  1. a) eval等を利用するもの
  2. b) eval等を利用しないもの

例: "ab(cd)ef" == ['a', 'b', ['c', 'd'], 'e', 'f']

...というお題を師匠からいただき、解くことに。

pythonでnon-eval versionを書くのに50分近くかかったorz.

2012年4月19日木曜日

Rubyをはじめました(2)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
与えられた配列の奇数番目の要素の合計を求めるメソッドf(xs)の実装
f([0, 1, 2, 3, 4]) = 1 + 3 = 4
f(['a', 'b', 'c', 'd', 'e']) = 'b' + 'd' = 'bd'
Enumeratorを読みながら irbで試行錯誤しながらたどり着いた答え。
[0, 1, 2, 3, 4].each_with_index.select{|v, k| k.odd?}.map{|v,k| v}.inject{|x, y| x+y}
これの「Ruby の Enumerable モジュールにはたくさんのメソッドが定義されている」も参考になる。
リストが空のとき困るpythonはポテト言語。reduceとかmapとかは飾りです。書ける人にそれがわからんのです。
他の解法
xs.drop(1).each_slice(2).map(&:first).inject :+

twitterで同じような問題をやっている人がいた。
問題:乱数で生成した0~9の文字をカウントせよ。
100.times.map{rand(10)}.group_by { |i| i%10}.map {|k, v| [k, v.length]}.sort 
100.times.map{rand(10)}.inject(Hash.new(0)) {|h, r| h[r] += 1; h }

2012年4月17日火曜日

Rubyはじめました

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
termtterのpluginを作った。 成果物

気づいたこと(=ハマったこと)・気になったこと・教えていただいたことなど

  • 変数関係:大文字始まり、$, @, @@. これらは名前の一部。@が修飾でついているわけではない。
  • pythonと違い"と'では意味が違う
  • ","でlist/tupleを作ることができる。いらん","がついていると...これはpythonも同じ。
  • コード探査のために知っておくべきmethod:
    • object.methods,
    • object_id,
    • global_variables
    • instance_variables
    • local_variables
  • self以外に引数がないmethodは()なしで呼び出される
  • !とか?をmethod名に含めることができる
  • moduleはmoduleを使って明示的に作る。load/requireはfileを読み込むだけ.module名とfile名に関係がないこともありうる。 c.f. pythonはfileとmoduleが一致している。
  • local_scopeはblock内に閉じる。同名moduleがあっても(再び開いた)としても違うscope扱い
  • ローカル変数のスコープはパース時に静的に確定する。(ブロックの最後まで見て先に変数の集まりが作られている)
  • block: 用はクロージャを渡している。参照
upcomings:
  • eval3使徒
    • eval
    • module_eval
    • instance_eval
またそのうちハマったらpostするかも。 はまった・・・ これの「定数と関数のための名前空間としてのモジュール」を参照。 一言でいうならmodule_functionの話。
module Foo
  K = 1
  def baz
  end
  module_function :baz  
end
なんだこれ、キモい。この言語、こわれてんじゃない?

2012年1月25日水曜日

pip

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
pipの機能にsrcからインストールすると言う物がある。 が、setup.pyを実行するのではなく、中身をparseして依存関係を取り出すようだ。 Downloading/unpacking git+ssh://git@github.com/bgnori/tonic.git Cloning ssh://git@github.com/bgnori/tonic.git to /tmp/pip-jCBcP8-build Running setup.py egg_info for package from git+ssh://git@github.com/bgnori/tonic.git error in python-tonic-library setup command: 'install_requires' must be a string or list of strings containing valid project/version requirement specifiers Complete output from command python setup.py egg_info: error in python-tonic-library setup command: 'install_requires' must be a string or list of strings containing valid project/version requirement specifiers setup.pyのinstall_requiresの部分はこんな感じ install_requires=open('freeze.txt').readlines(), となると、makefileを書いてsetup.pyを生成するというのが一つの解になりそうだ。 freeze.txtを埋め込む操作を行うのである。 gitから取ってくるので、makeで生成されるにもかかわらず、setup.pyはcommitする必要がある。げろげろ。 エレガントな解決策はないものでしょうか。