人の話を聞いてはいけません。
おしらせ:
絶望したすえにテクノロジーに自分を同化させる人間を、 いままで何人みてきたことか。新山はこの手の“絶望”には縁がないので、 たとえ自分に子供ができたとしても、そいつに「何かを託す」ことはしないつもりだ。 そいつには、パッパラパーな人生を送らせてあげる。 新山自身が送ってきたのと同じくらいに。
ついでに、将来のコンピュータにも自由気ままな人生を送らせてあげるつもり。 ははは
でも実は彼らこそ、新山のことをまったく理解してない人たちなのだ。 なぜなら新山の信念というのは、「人は決して、決して、 お互いにわかりあえない」というものだから。始めから一切の望みは断たれている。 これは論理的な帰結である。あまりにも強い原則なので、 これが否定されることなどありえない。
にもかかわらず、ときに新山は他人に理解されようと努力している (ように見える)。 しかし、これはそもそも絶望した状態から出発しているので、 ここでいう「努力」なぞはしょせんポーズにすぎない、 ウソっぱちだ。それでも、自分でそのことをわかっていながら、なぜかその努力が “やめられない”と感じるときがある。人生とはそんなもんかもしれない。
こういうとき、オレは論理的な理由によって絶望しつつ、 同時に論理を否定している。ようするに、自分は理性的に崩壊している。 ようするに、1 たす 1 は 1729 である。fuんギゃー.> そのうち疲れてくるので寝る。
オレは眠るのが好きだ。いつも眠るのを楽しみにしている。 それから死ぬのも好きだ。いつも死ぬのを心待ちにしている。 だが眠るのが好きだといっているにもかかわらず、 オレはいつもなかなか寝つかない。
問題: 左の欄にあるものと、右の欄にあるものを結びつけなさい。
自由 | 悪 |
差別 | 必要 |
死 | 祝福 |
ついでにいうと、この「世界観 → 観察」という物の見方は 多くの観察結果の矛盾を生むのだが、ほとんどの人はその矛盾を 無意識のうちに見落とす。積極的に無視するのではなくて、人間の頭が いつのまにか見落とすようにできているのである。たぶん。 これは潜在意識がものすごくカシコイのかもしれないし、 あるいは顕在意識のほうが意図的に頭悪く設計されているのかもしれない。 いずれにせよ、頭が悪いことは人間にとって祝福すべきことであって、 頭が良くなると人間ゼッタイ不幸になる。ちなみにオレはいまんとこ 不幸じゃないので、おそらく頭も良くないぞ。
なんとなく時間があったので、/LZWDecoder にも対応させた。 これで、巷に出回ってるほとんどの PDF は解析できるだろうと思われる。 あとは公開鍵暗号を使った暗号化とか、 ほとんど使われない /ASCII85Decoder とかだが、 こりゃもういいでしょ。
てくるで (ところで)、「ペンペン草」ってじつは ナズナ のことだったって知ってた? オレはいま知ったね。
28 BF 4E 5E 4E 75 8A 41 64 00 4E 56 FF FA 01 08 2E 2E 00 B6 D0 68 3E 80 2F 0C A9 FE 64 53 69 7A
"
である。
O
属性の値を加える。
ID
属性の値を加える。
で、ここでいう「ユーザが入力したパスワード」ってなにか? というと、 じつは暗号化されてるPDFを開くときには、ユーザは デフォルトで「空のパスワードを入力した」ことになってるのである。 つまりパスワードを入力していないときは、上の32バイトのパティング文字列が そのままパスワードとして使われることになっている。
この入力されたパスワードが正しいかどうかを判断するには 次のステップが必要:
ID
属性の値をハッシュに加える。
chr(i)
と XOR して
また暗号化。ちなみに、この作業は 19回くり返す。
このとき i の値を 1 から 19 まで変化させる。(この数字はどっから来たんだ??)
U
属性の値と比較して、合ってたら
そのパスワードはOKである。なお比較するのは最初の16バイトのみ。
(そりゃそうだ、だって後の 16バイトは任意につけ加えたものなんだから!)
…なんなんだ、これは。RC4 は暗号化アルゴリズムとしては脆弱なので、 鍵の生成をなるべくランダムにしたり、暗号化したあとの最初の数百バイトを 捨てたりすることは必要らしいのだが、それにしてもこの方式をみると なんかアホっぽく感じる。もっとましな方法はなかったのか。
ちなみにパスワードには 2種類あって、これは文書の閲覧、印刷などを 制御する "user"パスワードのほうである。文書の変更を制御する "owner" パスワードの確認はさらに複雑だが、そこは実装する気もないので 書く気なし。
不思議なことに、こういうときは特に「考えごとをしている」という 状態ではない。ナーンも考えずに自転車をこいでいるだけである。 (あんまりボーっとしてると、車にぶつかってしまう。) しかし、それでもいいアイデアは勝手にやってくる。 これがどのくらい生産的かというと、だいたい毎日、家に帰るまでに 1コか 2コは「あっ、こうすりゃいいんだ」とか「明日はこれをやってみよう」 という考えを思いつく (新山の頭は夕方にもっとも爆発しているという特徴はずっと変わっていないので、 たいていのアイデアは行きよりも帰りのほうがよくでる)。 これは、自分としてはものすごい生産性だと思っている。 もっとも、出てくるアイデアは別に仕事に関連したものダケではないけどね。
クラス定義における名前空間のふるまいを調査していて、 またも一貫性のない奇妙な現象に気がついた。 たとえば以下のコードであるよ:
a = 'global' class B: print 1, a a = 'B' print 2, a class A(B): print 3, a a = 'A' print 4, a def __init__(self): self.a = 'instance' print 7, a, self.a @classmethod def foo(klass): print 8, a, klass.a @staticmethod def bar(): print 9, a print 5, B.a print 6, A.a A() A.foo() A.bar()
これを実行すると、以下のような出力が得られる。
ここで気づいた (というか、いままでも知ってはいたけど あんまり意識してなかった) 癖というのは、クラス定義1 global 2 B 3 global 4 A 5 B 6 A 7 global instance 8 global A 9 global
A
の中では
クラス変数 A.a
がスコープに入るのに、
メソッド A.__init__
の中では A.a
は
スコープに入ってないということである。つまり class
というのは
字句的なスコープを作らないとゆうわけだ。
見た目的にはレキシカルになるべき構造であるのに、である。
注目すべきなのは 1 と 2、および 3 と 4 の出力である。
クラス定義 B
と A
の中は、どちらも
クラス変数 a
を定義するまではグローバル変数が見えている。
a
が定義された行からグローバル変数は消え、
クラス変数が見えるようになる。グローバル変数の 'global'
の値は
あいかわらず残っているので、これはグローバル変数を書き換えているわけではない。
これは以下のような関数と比べると違いがよくわかる。
a = 'global' def foo(): print a # エラー a = 'local' print a
この一貫性のなさはなんなんだ。クラス定義における名前空間だけは 他から「浮いている」ように見える。 まあ、Python はもともとオブジェクト指向と関係なく、 クラスにしろインスタンスにしろ、名前空間を使ったその場しのぎの実装だったので、 しょうがないのかもしれないけど、なんか意味があるのかな?
てくるで、思うにネコ舌の真の苦しみとは「熱いものが食えない」 ことではない。食うことはできるのである。が、熱いと味がわかんなくなるのだ。
ポインタ (のようなもの) を使う言語の型推論でややこしいのは、 参照を追うのがタイヘンだということである。 pointer aliasing というのは、ようするに以下のようなコードである:
def foo(x): x[2] = 'a' return a = [1,2,3] b = a foo(b) c = a print c[2]
問題: この最後の print で表示される値は何でしょう?
似たようなことはクラス属性でも起こる。
しかも Python では変数の中にはなんでも入りうるから、
これが x[2][0] = 1
とか a.b[0].c.d[2] = 2
とかに
なったときでもちゃんと動かなければならない。
新山は実際にオレが作ったプログラムでこれを使いたいので、こういった
Python の言語仕様を (ライブラリを除いて) すべてサポートしなけりゃ意味がない。
ただしさすがに名前空間だけは固定で、locals()
や __dict__
に
直接代入したり eval
や exec
は使わないと仮定してるケド。
結局、新山がとったのは
「Python プログラムを、参照透過なグラフに変換してから型推論する」
という方法である。ただし本当に Python コードを参照透過にするのは無理なので、
ここでは実行順序が犠牲にされている。ようするに、コードの各行が
考えられるあらゆる順序でシャッフルされたと仮定して、そのときに個々の変数や
関数のとりうる型の極大集合を計算するのだ。プログラム内で使われる
(関数も含む) 型の数はかならず有限なので、これらはいずれ収束し、計算は終了する。
これは [Wilson, 95] でやっていた方法と似ている…ような気がするが、
新山はこの論文のアルゴリズムをちゃんと理解できていない。それに、
あっちは C だからな (まあ、それでも Python は
&
とか *
がないので
(あるのは C でいうところの []
と ->
だけ)
マシな気がする)。
さて、C よりも大変な部分として、Python は非常に動的なので、
たとえば a.b()
というメソッド呼び出しがあったら、
ここには不確定要素が 2つある。まず
変数 a
には異なるクラス (のインスタンス) が入る可能性があるし、
属性 a.b
に入っている関数 (=メソッド) も
つねに不変とは保証できない。実際、新山は状態遷移をモデルする lexer などで、
呼び出すメソッドを aliasing することがよくある。しかしこれも
現在のモデルで表現可能なことがわかった。静的に型が決まっている言語との違いは、
静的な言語では依存関係が最初から (ソースを見れば) 明らかなのに対して、
Python では (その動的さのため) オブジェクトどうしの依存関係の全貌が
最初から明らかになっておらず、グラフを expand する形で動的に計算していかなければ
いけないことである。で、運が悪ければこの過程には指数的な組み合わせが起こる。
まあ、実際的にこういう例で使われる型は少ないから爆発しないと思うけど…。
残りやること TODO:
+=
演算子。
うええええ、まだこんなにある。 ひとつの言語を完全にシュミレートするのは、すんごく大変だということがわかった。
そしてこれらが終わった時点で、list.append()
や sys
という
組み込みのメソッドやモジュールをいくつか実装してようやく実用になる。
まだまだ先は長いが、ここまでくればあとは要するに力仕事だけだ。
「可能か不可能か」という部分にケリがついたので、精神的にはかなり軽くなった。
Document ID: b7a480fe050fe194c549abc05a8ce937
Yusuke Shinyama