動機
金は愛よりも重い - Qiita という記事を見た。
要は
puts "愛" < "金" #=> true
ということ。
爺ぃと呼ばれてもおかしくない歳の身としては,老婆心ながら,「ナウいヤングのプログラマーがこういう記事を読んで気持ちが荒んだりしないだろうか」といささか心配になった。
前途ある若者たちに希望を持ってもらいたい。それがロートルの願いだ。
"愛" < "金"
が真なのは一面の真実であるとしても,観点を変えれば別の結論が得られるのではあるまいか。
方針
重さを比べるのだから,文字をレンダリングしたときの塗りつぶし面積を比較してみよう。
グラフィックライブラリー cairo を使って文字をレンダリングする。黒地に白文字で書く。cairo にはピクセルを調べる機能はないので,いったん PNG ファイルに書き出す。
それを chunky_png という gem で開き,ピクセルの値を合算する。塗りつぶし面積に比例した値が得られる。
計数が終わったら画像ファイルは不要なので,初めから Tempfile
オブジェクトで書き出すようにする。
String の >
メソッドを,この塗りつぶし面積で比較するよう書き換えてしまう。2 文字以上の文字列は先頭文字で比較することにする。
リファインメントを使い,必要な箇所でのみ改変版に差し替えるようにする。
作譜
あらかじめ
$ gem install cairo
$ gem install chunky_png
しておく。
コードの本体はざっとこんな感じ。
require "cairo"
require "chunky_png"
require "tempfile"
require "test/unit"
module StringWeightCompare
refine String do
# String の <=> を書き換えても >, >=, <, <= 等は変わらないので
def <(other)
StringWeightCompare.weight(self) < StringWeightCompare.weight(other)
end
end
def self.weight(str)
size = 64
surface = Cairo::ImageSurface.new Cairo::FORMAT_RGB24, size, size
context = Cairo::Context.new surface
context.select_font_face "IPAexGothic"
context.set_font_size size
context.rectangle 0, 0, size, size
context.set_source_color :black
context.fill
context.move_to 0, size * 0.88
context.set_source_color :white
context.show_text str[0]
Tempfile.open do |f|
surface.write_to_png f.path
png = ChunkyPNG::Image.from_file f.path
# R 成分の合計値を取る
png.pixels.inject(0){|sum, color| sum + (color >> 24)}
end
end
end
using StringWeightCompare
class TestStringWeghtCompare < Test::Unit::TestCase
test "compare weight" do
assert "軽" < "重"
end
end
ユニットテストでも,「軽」より「重」のほうが重いことが確認できる。オーケー。
実行
いよいよ「金」と「愛」の重さを比べるときが来た。
いざ。
puts "金" < "愛" #=> true
うむ。
若人たちよ,これが真理だ!
蛇足
ふと気になって,他の文字でも比べてみた。
puts "命" < "金" #=> true
なんだかなー