2007年 11月 21日

Hpricot で一部の要素のみ通すフィルタ

require "rubygems"
require "hpricot"

def html_filter(parent, opts, &block)
	block = Proc.new {|x| x} unless block
	def escape(str)
		_escape = { '<'  => '&lt;', '>'  => '&gt;', '"' => '&quot;' }
		str.gsub(/#{_escape.keys.join("|")}/) {|m| _escape[m] }
	end

	ret = ""
	parent.each_child do |c|
		if c.elem?
			empty = Hpricot::ElementContent[c.name] == :EMPTY
			if opts.key?(c.name)
				ret << "<#{c.name}"
				ret << c.attributes.select {|k,v| opts[c.name].include? k}.map {|k,v| %| #{k}="#{escape v}"| }.join
				ret << "#{empty ? " /" : ""}>"
				ret << html_filter(c, opts, &block)
				ret << "</#{c.name}>" unless empty
			else
				ret << "&lt;#{c.name}"
				ret << c.attributes.map {|k,v| %| #{k}="#{escape v}"| }.join
				ret << "#{empty ? " /" : ""}>"
				ret << html_filter(c, opts, &block)
				ret << "&lt;/#{c.name}>" unless empty
			end
		else
			ret << block.call(c.to_s)
		end
	end
	ret
end

doc = Hpricot(inputs)
puts doc
puts html_filter(doc, opts) { |text|
	text.gsub(/¥n/, "<br />¥n")
}

こんなかんじかなぁ。ちょっときもいけど……


関係ないけど Mac でバックスラッシュをコピーして Fx にペーストすると円マークになってこまる。そのままペーストしてるけど、あとからコピペしようとすると使えないんだよね……

Ruby 1.9 のイテレータ (ちょーすごい)

で、そんなことより Mochkit.Iter の Synopsis の例を Ruby でやろうとおもったらいろいろ足りない感じ

(Enumerator は http://subtech.g.hatena.ne.jp/secondlife/20071104/1194144583 でちゃんと紹介されているよ! )

theSum = sum(takewhile(
        partial(operator.gt, 10),
        imap(
            partial(operator.mul, 2),
            count()
        )
    )
));

assert( theSum == (0 + 0 + 2 + 4 + 6 + 8) );

これこれ。

class Numeric
	def countup
		(self..(1.0/0)).to_enum
	end
end

module Enumerable
	def imap(&mapfun)
		Proc.new {|&block|
			self.each do |i|
				block.call mapfun.call(i)
			end
		}.to_enum(:call)
	end
end

# (sum はぬかしてる)
p 0.countup.imap {|x| x * 2}.take_while {|x| x < 10}

imap はなんか適当に書いてみたらこうなってしまったんだけどもっと簡単にかけたりするのかな…… 演算子の partial は定義しないといけないのと、ブロック書いたほうがはやいのでとりあえずとばし


countup はもっと簡単に書けないかとおもったけど微妙にめんどいっぽい。id:secondlife さんにもきいた。

# Float になっちゃう
p 0.to_enum(:step, 1.0/0).take(10)

# みじかいけど 1.0/0 (Infinity) がわかりにくい
p (0..(1.0/0)).to_enum.take(10)

# ながいよ
p lambda {|x,&block| loop { block.call(x); x += 1 } }.to_enum(:call, 0).take(10)

# Float::MAX が無限っぽくない
p 0.to_enum(:upto, Float::MAX).take(10)