2007年 11月 19日

強いたげられて生きているほうがあってるみたいだ。慣れちゃったんだなこういうのに

twitter にハマるってどういうことだろう

よくわからない。ハマるとかハマらないとかいうものなのかな。例えば「はてダにハマる」とか「LDR にハマる」とかいわないように、「twitter にハマる」っていうのには違和感を感じる。「mixi にハマる」も違和感がある。なんだろう。何にハマるんだろう。

XPathNSResolver のクロスブラウザとか

application/xhtml+xml の場合 (というより XML の場合)、XPath では namespace を持っているノードに対して prefix が必須 *1。すなわち HTML で //body と書いていたやつは //h:body とする必要があり、さらに h を http://www.w3.org/1999/xhtml (XHTML の NS URI) に関連付ける必要がある。

DOM 3 XPath では prefix の解決に XPathNSResolver というのを使っていて、これで prefix を関連づけてやる。

var context = document;
var resolver = function (prefix) {
    if (prefix == "h") return "http://www.w3.org/1999/xhtml";
    return null; # unknown
};
var expr = document.createExpression("//h:body", resolver);

で、これだと text/html のとき (全てのノードは名前空間が空) h:body はなにも選択しなくなってしまって悲しいので条件分岐する。

var context = document;
var resolver = function (prefix) {
    if (prefix == "h") return (document.documentElement.namespaceURI ? "http://www.w3.org/1999/xhtml" : "");
    return null; # unknown
};
var expr = document.createExpression("//h:body", resolver);

document.documentElement.namespaceURI でドキュメントが XML として扱われているかを判別してよさげなのをかえす。


基本的にはこれで十分だけど、XML として処理されるとき、dc: や rdf: とかもちゃんと選択できると嬉しい。毎回 resolver に条件を追加するのもいいけど、dc とか rdf とか、普通 prefix を変えたりしないので、こうする。

var context = document;
var resolver = function (prefix) {
    var c = document.createNSResolver(context).lookupNamespaceURI(prefix);
    if (c) return c;
    if (prefix == "h") return (document.documentElement.namespaceURI ? "http://www.w3.org/1999/xhtml" : "");
   return null; # unknown
};
var expr = document.createExpression("//h:body", resolver);

createNSResolver は Node をあたえると、そのノードが持っている名前 prefix と URI の対応をつかってリゾルバを作る。下位のノードは上位のノードの prefix/URI マッピングも持っている (というかスコープにある prefix は全部とってくる) ので、できるだけ下位のノードをわたす。

だいたい汎用的に使える resolver になった。ただ、xhtml の接頭辞に h を使うかはいまいち同意がとれないので、lookupNamespaceURI でみつからないなら全部 xhtml として扱うことにする。

var context = document;
var resolver = function (prefix) {
    var c = document.createNSResolver(context).lookupNamespaceURI(prefix);
    if (c) return c;
    return (document.documentElement.namespaceURI ? "http://www.w3.org/1999/xhtml" : "");
};
var expr = document.createExpression("//h:body", resolver);

割と嬉しい resolver になった。


ちなみに、いろいろ調べているうちに Opera の非互換を踏んでしまった。Opera (9.24/9.5) では resolver が "" (空文字列) をかえすと、null を返したときと同じように NAMESPACE_ERR を投げてしまう (Safari や Gecko では名前空間が空ということにしてくれる)。つまり Opera だと全適用のユーザスクリプトで XPath つかうときはページが XML か HTML かで式自体を変えないといけないのでめんどい。とかいうのを IRC でごちゃごちゃ言ってたら Kuruma さんに報告してもらった。ありがとう!

あと XML の仕様では「名前空間に関連づいていないノードの名前空間」の名前が定義されていないみたい。no value (Namespace in XML) とかだったり null namespace (XSLT) だったりする。

drry さんが document を createNSResolver にわたすと名前空間解決できないことがあるよっていっていたので (よくわかってなかたw)、さらに追試してみたところ Opera と Fx2 では document をわたしたとき documentElement に解決を委譲しないらしいことがわかった (GranParadiso では修正済らしい https://bugzilla.mozilla.org/show_bug.cgi?id=307465 あと Safari では問題ない)

     case DOCUMENT_NODE: 
          return documentElement.lookupNamespaceURI(prefix) 
http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html

この部分が正しく実装されてない。Opera--

*1: 名前空間を持っているノードは prefix なしでは絶対に選択できない

Ruby, Proc#curry

引数全部カリー化したい (一回だけじゃなくて)。可変長引数は考慮しない。

Lambda = Proc
class Lambda
	def curry
		s = self
		args = []
		(1...self.arity).inject(lambda {|x| p args; s[*(args+[x])] }) {|r,i|
			lambda {|x|
				args = args + [x]
				r
			}
		}
	end
end

sum = lambda {|x, y, z| x - y + z }.curry
p sum[1][2][3] # 2
p sum[1][2][3] # args が破壊されて失敗

なんかいろいろやってみたけどうまくいかないので eval に逃げ

Lambda = Proc
class Lambda
	def curry
		s = <<-EOS.gsub(/^¥t{3}/, "")
			lambda {|al|
				args = [#{(1...self.arity).inject(""){|r,i|r<<"a#{i}, "}}al]
				ObjectSpace._id2ref(#{self.object_id})[*args]
			}
		EOS
		eval (1...self.arity).inject(s) {|r,i|
			<<-EOS.gsub(/^¥t{4}/, "")
				lambda {|a#{self.arity-1-i+1}|
					#{r}
				}
			EOS
		}
	end
end

sum = lambda {|x, y, z| x - y + z }.curry
p sum[1][2][3] # 2
p (sum1 = sum[1])
p sum1[2][3]
p lambda {|x, y, z, a| x + y + z + a}.curry[1][2][3][4]


これ GC で死ぬよね。参照保持しないと

もっと簡単に書けた。あと ObjectSpace._id2ref は $SAFE 高いとつかえない。

class Lambda
	def curry
		s = <<-EOS.gsub(/^\t{3}/, "")
			lambda {|al|
				args = [#{(1...self.arity).inject(""){|r,i|r<<"a#{i}, "}}al]
				self[*args]
			}
		EOS
		instance_eval (1...self.arity).inject(s) {|r,i|
			<<-EOS.gsub(/^\t{4}/, "")
				lambda {|a#{self.arity-1-i+1}|
					#{r}
				}
			EOS
		}
	end
end

Ruby チャーチ数

そのまま実装してみます。チャーチ数は自然数 n の場合、引数 f をとり、引数 x に対し f を n 回適用する関数を返す高階関数らしいです。

\f x -> x       -- 0
\f x -> f x     -- 1
\f x -> f (f x) -- 2

数としてもどしたいときは

-- cn はチャーチ数
cn (\x -> x + 1) 0

とかやるとたぶん数になります。

class ChurchNumber < Lambda

	# ¥m n f x -> m f (n f x)
	def +(m)
		n = self
		m = m.to_church_number
		ChurchNumber.new {|f|
			lambda {|x|
				m[f][n[f][x]]
			}
		}
	end

	# ¥n f x -> f (n f x)
	def succ
		n = self
		ChurchNumber.new {|f|
			lambda {|x|
				f[n[f][x]]
			}
		}
	end

	def to_i
		self[lambda {|x| x + 1 }][0]
	end

	def coerce(number)
		[number, self.to_i]
	end

	def inspect
		# "#<%s:0x%08x:%d>" % [self.class, self.object_id, self.to_i]
		"#<%s:%d>" % [self.class, self.to_i]
	end

	def to_church_number
		self
	end
end

class Integer
	def to_church_number
		n = self
		ChurchNumber.new {|f|
			lambda {|x|
				(1..n).inject(x) {|r,i| f[r] }
			}
		}
	end
end

p 1.to_church_number      #=> #<ChurchNumber:1>
p 1.to_church_number.succ #=> #<ChurchNumber:2>
p 2 + 1.to_church_number  #=> 3
p 2.to_church_number + 1  #=> #<ChurchNumber:3>

(0..10).each do |i|
	p [i.to_church_number, i.to_church_number.to_i]
end

ラムダ式の読みかた

わかってなくて苦労したけどようはλを \ にして . を -> にすれば Haskell の記法として読めるみたいだ (普通逆なんだろうけど)

Lazy K の IO リスト

Lazy K のプログラムは Input リストを引数にとり、Output リストを出力する関数とみることができます。でもって空の Lazy K プログラムは I として扱われるため (というか何か書くと I の引数として評価されてく) Lazy K での cat は 0 byte になります。(Input をそのまま Output するので I でいい)

Lazy K のリストは Lisp のようにペアのいれこになっていて、一つのペア (X . Y) は (lambda (f) (f X Y))) と表現するらしいです。

Ruby で Lazy K の Input をそのまんま実装すると

def inputlist(l)
	lambda {|f|
		f[(l.first || 256).to_church_number][inputlist(l[1..-1] || [])]
	}
end

il = inputlist([72, 101, 108, 108, 111])
S = lambda {|x, y, z| x[z][y[z]] }.curry
K = lambda {|x, y| x }.curry
I = lambda {|x| x }

car = il[K]    #=> チャーチ数 72 (car il)
cdr = il[S[K]] #=> (cdr il)
cdr[K]         #=> チャーチ数 101 (car (cdr il))

こんな感じになりました。 Lazy K の Input リストは無限リスト (EOF 以降は 256) になっています。