「われ敗れたり」を読んだ

故・米長永世棋聖の「われ敗れたり」を本屋で立ち読みしてて首が痛くなってきたので衝動買いしました. 電王戦でボンクラーズに敗れた話がまとめられています. 将棋の棚にあったけど情報系の読み物としても十分おもしろいなぁと思いました(確かに将棋のルール知らないと一部読めない所もあるけど).

あの対局の中で,策のポイントみたいなものが3つ紹介されていました. それについては本のネタバレなので明かせないですが, 計算機知能の本質的な弱点なのかなと思い,深いと思いました.

あとは企画の話とか勝負師的な面とかメディアについてとか. ここまでで文庫本4冊分ぐらいの価値があると思います. それに棋士と開発者のインタビューが載ってる.

技術書ばっかりで脳が凝り固まってしまった気分だったので買って良かった.

MIDIファイルを生成する

Python3は文字列とかバイト列とかの扱いがすっきりした感じがするので練習がてらStandardMIDIFileを出力させてみた.

:::python
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
import copy
import heapq

class SMF(object):
    '''Standard MIDI File
    '''
    def __init__(self, fmt=1, division=480):
        self.division = division
        self.format = fmt
        self.tracks = []
    def measuretick(self, division):
        return divmod(abs_time, division * 4)
    def abs_time(self, measure, tick):
        return measure * 4 + tick
    def __bytes__(self):
        # header signature
        result = bytearray(b"MThd")
        # header size
        result += (6).to_bytes(4, byteorder='big')
        # format
        result += self.format.to_bytes(2, byteorder='big')
        # number of tracks
        if self.format == 0:
            result += (1).to_bytes(2, byteorder='big')
        elif self.format == 1:
            result += len(self.tracks).to_bytes(2, byteorder='big')
        # time division
        result += self.division.to_bytes(2, byteorder='big')
        if self.format == 0:
            sumtrack = Track()
            for t in self.tracks:
                sumtrack.events += t.events
            result += bytes(sumtrack)
        elif self.format == 1:
            for t in self.tracks:
                result += bytes(t)
        else:
            raise ValueError("format is invalid: %d".format(self.format))
        return bytes(result)

class Track(object):
    '''Track data
    '''
    def __init__(self):
        self.events = []
    def to_vbytes(self, dt):
        '''convert to the variable length format.
        '''
        result = [dt % 0x80]
        dt //= 0x80
        while dt > 0:
            result += [dt % 0x80 | 0x80]
            dt //= 0x80
        return bytes(result[::-1])
    def _block_bytes(self):
        result = bytearray()
        eventheap = copy.deepcopy(self.events)
        heapq.heapify(eventheap)
        t = 0
        runninng = None
        while len(eventheap) > 0:
            e = heapq.heappop(eventheap)
            delta = e.abs_time - t
            t = e.abs_time
            result += self.to_vbytes(delta)
            eventbytes = bytes(e)
            if runninng == eventbytes[0] and runninng != b"\xF0" and runninng != b"\xFF":
                result += eventbytes[1:]
            else:
                result += eventbytes
                runninng = eventbytes[0]
            if e.has_after():
                heapq.heappush(eventheap, e.after())
        # track end
        result += b"\x00\xFF\x2F\x00"
        return result
    def __bytes__(self):
        # track signature
        result = bytearray(b"MTrk")
        blocks = self._block_bytes()
        # block size
        result += len(blocks).to_bytes(4, byteorder="big")
        result += blocks
        return bytes(result)

class Event(object):
    def __init__(self, t=0):
        self.abs_time = t
    def __lt__(self, o):
        return self.abs_time < o.abs_time
    def __le__(self, o):
        return self.abs_time <= o.abs_time
    def __gt__(self, o):
        return self.abs_time > o.abs_time
    def __ge__(self, o):
        return self.abs_time >= o.abs_time
    def has_after(self):
        return hasattr(self, "after")

# SysEx Events

class GMSystemOn(Event):
    def __bytes__(self):
        return b"\xF0\x05\x7E\x7F\x09\x01\xF7"

# Meta Events

class Tempo(Event):
    def __init__(self, tempo=120, t=0):
        Event.__init__(self, t)
        if tempo <= 0:
            tempo = 120
        self.microsec = 60 * 1000000 // tempo
    def __bytes__(self):
        return b"\xFF\x51\x03" + self.microsec.to_bytes(3, "big")

# MIDI Events

class ControllChange(Event):
    def __init__(self, t, ch, d):
        Event.__init__(self, t)
        self.ch = ch
        self.d = d
    def __bytes__(self):
        return bytes([0xB0 | self.ch, self.ccnum, self.d])
class BankSelectMSB(ControllChange):
    ccnum = 0x00
class Modulation(ControllChange):
    ccnum = 0x01
class BreathController(ControllChange):
    ccnum = 0x02
class FootController(ControllChange):
    ccnum = 0x04
class Portamento(ControllChange):
    ccnum = 0x05
class DataEntryMSB(ControllChange):
    ccnum = 0x06
class Volume(ControllChange):
    ccnum = 0x07
class Balance(ControllChange):
    ccnum = 0x08
class Pan(ControllChange):
    ccnum = 0x0a
class Expression(ControllChange):
    ccnum = 0x0b
class Hold(ControllChange):
    ccnum = 0x40
class Portamento(ControllChange):
    ccnum = 0x41
class Sostenuto(ControllChange):
    ccnum = 0x42
class Soft(ControllChange):
    ccnum = 0x43
class Resonance(ControllChange):
    ccnum = 0x47
class Release(ControllChange):
    ccnum = 0x48
class Atack(ControllChange):
    ccnum = 0x49
class CutOff(ControllChange):
    ccnum = 0x4a

class NoteOff(Event):
    def __init__(self, t, ch, n):
        Event.__init__(self, t)
        self.ch = ch
        self.n = n
    def __bytes__(self):
        # use 0x90 for runninng status
        return bytes([0x90 | self.ch, self.n, 0x00])

class NoteOn(Event):
    def __init__(self, t, ch, n, vel, gate):
        Event.__init__(self, t)
        self.ch = ch
        self.n = n
        self.vel = vel
        self.gate = gate
    def __bytes__(self):
        return bytes([0x90 | self.ch, self.n, self.vel])
    def after(self):
        return NoteOff(self.abs_time + self.gate, self.ch, self.n)

class ProgramChange(Event):
    def __init__(self, t, ch, p):
        Event.__init__(self, t)
        self.ch = ch
        self.p = p
    def __bytes__(self):
        return bytes([0xC0 | self.ch, self.p])

import unittest

class SMFTest(unittest.TestCase):
    def test_smf_bytes(self):
        f = SMF()
        result = bytes(f)
        print(result)
        self.assertIsInstance(result, bytes)
        self.assertEqual(result[:4], b"MThd")
        print(int.from_bytes(result[4:8], "big"))
class TrackTest(unittest.TestCase):
    def test_to_vbytes(self):
        t = Track()
        self.assertEqual(t.to_vbytes(0x00000000), b"\x00")
        self.assertEqual(t.to_vbytes(0x00000040), b"\x40")
        self.assertEqual(t.to_vbytes(0x0000007F), b"\x7F")
        self.assertEqual(t.to_vbytes(0x00000080), b"\x81\x00")
        self.assertEqual(t.to_vbytes(0x00002000), b"\xC0\x00")
        self.assertEqual(t.to_vbytes(0x00003FFF), b"\xFF\x7F")
        self.assertEqual(t.to_vbytes(0x00004000), b"\x81\x80\x00")
        self.assertEqual(t.to_vbytes(0x00100000), b"\xC0\x80\x00")
        self.assertEqual(t.to_vbytes(0x001FFFFF), b"\xFF\xFF\x7F")
        self.assertEqual(t.to_vbytes(0x00200000), b"\x81\x80\x80\x00")
        self.assertEqual(t.to_vbytes(0x08000000), b"\xC0\x80\x80\x00")
        self.assertEqual(t.to_vbytes(0x0FFFFFFF), b"\xFF\xFF\xFF\x7F")

if __name__ == '__main__':
#   unittest.main()
    smf = SMF(fmt=1)
    smf.tracks.append(Track())
    smf.tracks[0].events.append(GMSystemOn(0))
    smf.tracks[0].events.append(Tempo(tempo=120, t=40))
    smf.tracks.append(Track())
    ch = 1
    smf.tracks[1].events.append(Volume(40, ch, 96))
    smf.tracks[1].events.append(ProgramChange(60, ch, 0x39))
    smf.tracks[1].events.append(NoteOn(0 + 480 * 4, ch, 0x3C, 127, 480))
    smf.tracks[1].events.append(NoteOn(0 + 480 * 4, ch, 0x44-12, 127, 480))
    smf.tracks[1].events.append(NoteOn(480 + 480 * 4, ch, 0x3E, 127, 480))
    smf.tracks[1].events.append(NoteOn(480 + 480 * 4, ch, 0x45-12, 127, 480))
    print("write to test.mid...")
    with open("test.mid", "wb") as f:
        f.write(bytes(smf))
    print("OK!")

参考

middlewareで簡単アクセス解析

Djangoのmiddlewareって意外と単純なんですね.

process_reques から request データベースに放り込むようにすると簡単なアクセス解析アプリが作れる. 見る方は templates/admin/[アプリ名]/change_list.html あたりに.

今まで ssh で入ってログ眺めたりスクリプト走らせてたりしてたけど, アプリと連携できるようになるので便利.

python-whois と組み合わせたら良いかも.

django-skelを使うようにした

リファクタリングになったし表示速度にも効いてる気がする.

Djangoが分かってきたけど綺麗に作るには……?という人には非常に良いと思う.

settings/prod.py あたりをちょっと変えたら VPS な人も大丈夫です. 潔癖な方は reqs/ あたりも.

特に apps ディレクトリのアイディア見た時はなるほどと思った.

あと, settings.py に DEBUG = socket.gethostname() == '開発環境のホスト名' みたいなことしてたけど, DJANGO_SETTINGS_MODULE を使うのが正しかったのか.

あと, CACHE とか compress とか. こんなに手軽に高速化できるとは.

Djangoで更新する度に日付が変わるバグに遭遇した

DjangoでDateTimeFieldを使っていたら, なぜか管理画面で日付が更新ボタンを押す度に変わる. それも±9:00で.

modelのメソッドでバグ出しちゃったかなーと思ったけどそうでもなさそう.

結局settings.pyで TIME_ZONE = 'Asia/Tokyo' としてたせいでした. 'Japan'に変えたら治った.

なんで再現性のないバグになっちゃうんだろう…

腐ったmakeを支える技術

あんまり身に付けたくない力だけど付いてしまったものは仕方がない. これ以上C/C++のビルドごときで時間を取られたくないので文章化したい(パズルみたいでちょっと楽しいけど).

なぜビルドが通らないのか

  1. 依存するライブラリが無い
  2. 古い文法や関数が使われている
  3. コマンドのバージョンが変わっている
  4. 腐ってやがる

1. 依存するライブラリが無い

configureでエラー

ラッキーです. ディストリのリポジトリ,もしくは本家からダウンロードしましょう.

リンカエラー

関数名で調べてみましょう. 何か見つかるかもしれません.

ネット上に無い

あきらめてそいつをbuildしない方法を考えましょう.

2. 古い文法や関数が使われている

gcc3/4の違いでいろいろあるっぽい.

プロトタイプ宣言が関数内部に書かれている

3では通りますが4だと構造体と勘違いした感じのエラーを吐きます. グローバル領域に動かしましょう. staticをつけると安全です.

古いスタイルの宣言

VC++だとエラーになるらしいです. また,gcc-3.xは引数リストが一致しなくてもプロトタイプ宣言として有効らしいので, 4.xでコンパイルすると名前被りでエラーになります. 昔適当に書いていた野郎を恨みましょう.

varargs.h

冒頭のツイートの時に最も困ったのがこれ. 現在は stdarg.h があって varargs.h をincludeするとご丁寧にエラーにしてくれる.

解決策としては

  • 昔のコンパイラをインストール
  • varargs.h を自作する(探したらあるかも)
  • ソースを修正する

3. コマンドのバージョンが変わっている

makeやスクリプトの内部で起動するコマンドのパスやバージョンが変わって,うまく動かない場合があります. gccアーキテクチャの指定なんかは gcc -march=native で良いと思うので, エラーを吐いてるなら置き換えましょう.

bison とか flex っぽい拡張子でエラー吐くとかなり苦しい.

4. 腐ってやがる

f**k!!

エラーメッセージを注意深く読むしかありません. 何か分かるかも.

実行時エラー

  • デバッガを使う
  • strcpy -> strncpy など
  • const をつける
  • 最適化オプションを外す

逃げる

あなたの目的はビルドを通すことではなく問題を解決することのはずです. 他の手段を探した方が効率が良いかもしれません.

将来迷惑をかけないために

  • configure をちゃんと書く
  • READMEを作る
  • 作った年ぐらいは書いておく
  • 標準的な書き方に従う
  • const をつける
  • 保守する

「昔の環境で使っている人ガー……」ってのはあまり良い言い訳ではないと思う. 趣味とかで作ったものを保守するのってめんどくさいけどね.