点字が拓く新しい世界

投稿者 nanki 2013-04-15 16:55:00 GMT

特に理由もなく一年以上放置していましたが、特に理由もなく再開します。

最近、仮想端末をiTerm2にしたので、xterm-256使えるぞと思って色々遊んでいました。(*OSX標準のターミナルもいつの間にかxterm-256対応していた)

Unicodeには点字を表現するための文字群があり、2x3の点字64種類とそれを含む2x4の点字256種類で構成されています。 これはUnicode中で自由に制御できる最も解像度の高い文字群ではないかと思い、xterm-256と合わせて利用してみることにしました。

# ちなみにこんなのです
⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿

Step 1

まずは点字のみで、二値画像を表現してみます。 ちょうど新しい職場でPythonを使い始める時期だったのでPythonで、二値画像としては手頃なマンデルブロ集合を使います。

ポイントはWikipediaにもありますが、2x3の点字が最初のほうに固まっているので、ビットを入れ替える必要があるところとマンデルブロの綴りくらいでしょうか。

mandelbrot

Step 2

Pythonにも慣れてきたので次はいよいよxterm-256を使いますが、いきなりカラーは難しそうなのでグレイスケールを使います。

The 256 color mode of xtermによれば、xterm-256はシステムカラー16色、6階調RGBの216色、グレイスケール24段階の計256色で構成されており、 以下のシーケンスで前景色、背景色が指定できます。(ここだけRubyです)

print "\e[38;5;#{color}m" # 前景色
print "\e[48;5;#{color}m" # 背景色
# グラデーションがでます
$ ruby -e '24.times{|i|print"\e[48;5;#{232+i}m "};puts'

そうしてできあがったのが以下のコードです。

基本的には、切り出された2x4pixelsに対して最も暗い色と明るい色を前景色/背景色に選び、各pixelをどちらかに振り分けるだけなんですが、

  • 誤差拡散法
  • なるべく前景色/背景色が同じにならないようにする
  • ガンマ調整(のようなもの)

といった工夫によりかなり画質を向上させています。 興味のある方はコードを弄って、どれくらい画質に影響を与えているか見てみると楽しいかもしれません。

lena

Step 3

カラー対応といいたい所ですが、3次元から1次元へ削減を行わないといけなかったり、人間の色覚モデルを考慮するのが面倒そうだったりしてまだやってないです。


関西闇Ruby会議とFFI::Ruby

投稿者 nanki 2011-11-15 14:12:00 GMT

関西闇Ruby会議で発表しました。

内容は最近使う機会の増えたruby-ffi(フィッフィ)の紹介。 その時のスライドです。

ちょっとまとめきれなくて最後のほうにごちゃごちゃと書いてしまっていますが、C/Ruby間で独自の型変換を作れるとあったので試しにVALUE型をRubyのオブジェクトに変換するffi-rubyというのを作ってみました。 実用的なかおりがするけどあんまり使い途ない。

VALUE型の変換これであってるのかなぁ。簡単な恒等性のテストにはパスしてるみたいだけど。


Mac OS X でFrama-Cを使ってRubyを静的解析するメモ

投稿者 nanki 2011-11-06 18:11:00 GMT

@ikegami__さんのFrama-Cを使った実験が面白そうなので試してみた。

試験環境はMacBookAir/Mac OS X Lion/ruby192

インストール

$ brew install ocaml
$ wget http://frama-c.com/download/frama-c-Nitrogen-20111001.tar.gz
$ tar zxf frama-c-Nitrogen-20111001.tar.gz
$ cd frama-c-Nitrogen-20111001
$ ./configure --prefix=...
$ make
$ make install

使ってみる

Rubyのソースツリーに移動する。

$ cd ruby
$ ./configure

記事を参考に、.ext/include/ARCH_NAME/ruby/config.h に以下の行を追加。

#ifndef TRUE
  #define TRUE 1
#endif
#ifndef FALSE
  #define FALSE 0
#endif

解析対象ファイルの一覧を作っておく。

$ vi mainobj
array.c bignum.c class.c compar.c complex.c dir.c dln_find.c enum.c enumerator.c error.c eval.c load.c proc.c file.c gc.c hash.c inits.c io.c marshal.c math.c node.c numeric.c object.c pack.c parse.c process.c random.c range.c rational.c re.c regcomp.c regenc.c regerror.c regexec.c regparse.c regsyntax.c ruby.c safe.c signal.c sprintf.c st.c strftime.c string.c struct.c time.c transcode.c util.c variable.c compile.c debug.c iseq.c vm.c vm_dump.c thread.c cont.c

一覧は、common.mk中のCOMMONOBJSを参考にしている。

Frama-C Nitrogen 20111001 で ruby-1.9.2-p290 を検査するときにどうにもならなさそうな事であげられているファイルのいくつかは、直接コンパイルされることを想定して書かれていないので失敗するようだ。

解析開始

$ frama-c \
  -cpp-extra-args="-U__BLOCKS__ -DRUBY_EXPORT -I./.ext/include/ARCH_NAME -Iinclude" \
  -machdep x86_64 \
  -save ruby.session \
  `cat mainobj` 

-U__BLOCKS__はSnowLeopardから導入されたBlocks(Cの文法を拡張している)を無効にするため。これを無効にしないとparseが失敗する。

-machdep x86_64はARCH_NAMEにあわせたものを$ frama-c -machdep helpの結果から選ぶ。

$ frame-c -load ruby.session -metrics
...(ry)...
[metrics] Syntactic metrics
          -----------------
          ** Defined functions (6297):
          ...(ry)...

          ** Undefined functions (297):
          ...(ry)...

          ** Potential entry points (1694):
          ...(ry)...

          SLOC: 144947
          Number of if statements: 25635
          Number of assignments: 40691
          Number of loops: 3651
          Number of calls: 31176
          Number of gotos: 7496
          Number of pointer access: 50736

$ frame-c -load ruby.session -users -main rb_ary_new
...(ry)...
[users] ====== DISPLAYING USERS ======
        ary_alloc: rb_newobj
        ary_new: rb_newobj ary_alloc
        rb_ary_new2: rb_newobj ary_alloc ary_new
        rb_ary_new: rb_newobj rb_ary_new2 ary_alloc ary_new
        ====== END OF USERS ==========
参考:

CHM viewer on terminal

投稿者 nanki 2011-09-04 05:31:00 GMT

MacBookPro壊れてLinux使ってた時にGUIのものしか見当たらなかったので。

名前も未定です。

Rubyリファレンスマニュアルのchmは文字コード変なので悲しい。


FizzBuzzの世界

投稿者 nanki 2011-08-25 18:20:00 GMT

ちょっと興味深いfizzbuzzを発見したので。


Guardを使ったCoffeeScript開発

投稿者 nanki 2011-05-10 16:29:00 GMT

guardはファイルの変更を検知して、決められたタスクを実行するためのツールである。 Mac OS X / Linux / Windows それぞれのファイルシステム変更検知APIに対応しており、それ以外のプラットフォームではポーリングにて変更検知を行う。

$ gem install rb-fsevent # Mac OS X の場合
$ gem install guard
$ gem install guard-coffeescript
$ guard init coffeescript
$ vi Guardfile
guard 'coffeescript', :input => 'src/', :output => 'dist/'
$ guard start

こうすることで、src/ 以下の.coffeeファイルに変更があった場合即座にコンパイルされ、dist/以下に.jsファイルが生成される。

なお、rb-fseventは入れなくても動くがその場合はポーリングになる。 Mac以外のOSではこうするらしい。

$ gem install rb-inotify # Linuxの場合
$ gem install rb-fchange # Windowsの場合

guard-coffeescriptの他にも様々なguardがあるようです。

参考:

Mac OS X で変な漢字を探す亖つの方法

投稿者 nanki 2011-04-20 07:05:00 GMT

元々の話題は、火が三つの漢字を見た事あるけど見つからない、というものから。

1. 手書き文字入力 / ⌃⌥ Space / Ctrl+Shift+Space

手書き文字入力

詳しくは下記URLの記事を参照。

2. ⌃1, ⌃2 / Ctrl+1, Ctrl+2

部品の共通な漢字を検索 対象となる文字を選択後、Ctrl+1を押す

関連文字に変換 対象となる文字を選択後、Ctrl+2を押す

3. 文字ビューア

「文字ビューアを表示」を選択

記号など探すには便利。地道に探す。 文字ビューア

4. CHISE/漢字構造情報データベースを使う

CHISE IDS Findを使って検索するか、CHISEプロジェクトからデータベースをダウンロードして自分で探す。

今回のように三つの文字が並んでいる漢字を探す場合、例えば”森”、という字なら、

… 森 ⿱木⿰木木

という感じの表現で入っているはずなので、UTF-8を正しく扱えるシステム上で /\t⿱(.)⿰\1\1$/ という正規表現を使えば、同じ漢字が三つ並んだ漢字の一覧を得ることができる。 上記は簡単だが手抜きなやり方で、完全性に関してはいくつか問題がある。

  1. 文字に対する表現の曖昧性 例えば、森に対しては⿱木林 / ⿱木⿰木木の二つの表現が考えられる。検索時にこれを考慮しなくてはならない。(同じ漢字が四つならぶ時など考えたくない…)

  2. 包摂の扱い 同じ漢字なんだけど字形が違う場合、例えば「」の各部品はトメ、ハライの違いを考慮してデータベースに登録されており、上記のような単純な正規表現ではこれは扱う事ができない。

というわけで、今回見つけられた同じ漢字が三つの文字(ただしSnowLeopardで表示されるものに限る)は

post先のサーバがどれかの文字を正しく扱う事ができず途中で切れてしまったので画像で失礼。

参考:

ObjectSpaceダンプ

投稿者 nanki 2011-02-17 14:03:00 GMT
def top_objects(num=20)
  ObjectSpace.each_object.inject(Hash.new{0}){|h,o|h[o.class]+=1;h}.sort_by{|k,v|-v}.first(num).each{|k,v|puts "#{k}\t#{v}"}
end

GC.start
top_objects

--
String  577159
Array  163891
Hash  93468
Time  50353
ActiveSupport::TimeWithZone  50129
ActiveModel::Errors  23365
TZInfo::TimezonePeriod  23340
ActiveSupport::HashWithIndifferentAccess  23236
RubyVM::InstructionSequence  12484
MIME::Type  1197
MatchData  1183
ActiveModel::AttributeMethods::ClassMethods::AttributeMethodMatcher::AttributeMethodMatch  1128
Class  1101
Regexp  972
Proc  704
Module  655
Gem::Version  619

GCを生き残ったインスタンス数をダンプ。


高度すぎるコードジェネレータ WLang

投稿者 nanki 2010-10-10 00:14:00 GMT

みなさんはコードジェネレータを使った事があるでしょうか。私はあります。

SQLにHTML, JavaScript, はてはPHPまで、Rubyでコードジェネレータを使う機会は増すばかりです。

さて、Rubyでコードジェネレーションと言えばERbを使うことが多いですが、それで充分なのでしょうか。 他の選択肢はどうでしょうか。

今日は、wlangというコードジェネレータを紹介します。

template engine?

次のサンプルコードを見てください。 とてもシンプルなHTMLテンプレートエンジンのように見えます。

require 'wlang'

puts WLang::instantiate(<<EOS, {:name => "O'Reilly & Animals", :animals => %w(Koala Rhino Giraffe)}, "wlang/xhtml")
<h1>${name}</h1>
<div id="{name}">
  <img alt='{name}' src="http://....."/>
  <ul>
    *{animals as animal}{${animal}}{, }
  </ul>
</div>
EOS

このプログラムの出力結果はこうなります。

<h1>O'Reilly &amp; Animals</h1>
<div id="O'Reilly & Animals">
  <img alt='O\'Reilly & Animals' src="http://....."/>
  Koala, Rhino, Giraffe
</div>

コンテキストに合わせて正しく、'&がエスケープされているのがわかります。 また、記法にはあまり馴染みがありませんが、配列の展開もうまく動いています。

wlang >> template engine

次にwlangの仕組みを見ていきます。 wlangでは、wlangタグを使って、テンプレートを記述します。 wlangタグは次のような形式です。

記号{.....}{.....}{.....}

後ろの{}はblockと呼ばれ、任意の個数持つ事ができます。 記号はたとえば $, *などです。

そう、実は先のテンプレートで出現した、${...}, "{...}, '{...}, *{...}{...}{...} は全て、wlangタグだったのでした。

(ちなみに、”{name}” の末尾の”はwlangタグとは関係なく、ただの文字列です。 これは <div id="{name}'>が文法エラーを出さずに動作する事で確認できます。このセンス嫌いじゃない)

なにやらあやしいかおりがしてきました。

wlang = programming language?

次に、WLang::instantiateの第三引数は”wlang/xhtml”となっていますが、WLangではこれをdialect(方言、なまりの意味)と呼んでおり、当然変えられます。

require 'wlang'

sql = "SELECT * FROM books WHERE name='{name}';"
values = {:name => "O'Reilly"}

puts WLang::instantiate(sql, values, "wlang/sql")
puts WLang::instantiate(sql, values, "wlang/sql/sybase")

# => SELECT * FROM books WHERE name = 'O\'Reilly';
# => SELECT * FROM books WHERE name = 'O''Reilly';

このように、dialect毎に'{...}タグの動作が変わるわけです。

dialectは複数のRuleSetとEncoderSetで定義されています。 RuleSetはタグの機能をオーバライドしたり新たなタグを定義します。 EncoderSetはdialect固有のencoding、つまり文字列の変換方法を定義していて、 ^{..encoding..}{...}というタグで呼び出す事ができます。

以下は大文字に変換するencodingの例です。

# instantiateとか省略します
^{plain-text/upcase}{wlang}
=> WLANG

これが例えばwlang/xhtml dialect であれば wlang/xhtml/main-encoding, wlang/xhtml/single-quoting, wlang/xhtml/double-quoting などが定義され、それぞれ違うエスケープがされるようになっています。

先のwlang/sqlwlang/sql/sybaseの結果の違いも、ここで決まっています。

勘のいい方ならお気づきだと思いますが、${...}, '{...}, "{...}はそれぞれdialectのmain-encoding, single-quoting, double-quotingを呼び出すためのショートカットになっています。

polyglot!

この様な拡張可能かつ再利用可能な変換規則を用意することで、Rubyの文字列を埋め込んだJavaScript、を埋め込んだxhtmlを埋め込んだRubyスクリプト、などといった複雑なコード生成を行う場合でも、 エスケープに悩まされることなく気楽にコード生成を行う事ができるwlangかっこいい!

でも、encodingの実装にバグがあるっぽくて、まだそれは無理っぽい。

参考:

Rubyでfinalizerの順番を制御する

投稿者 nanki 2010-09-21 00:55:00 GMT

SWIGでLLVMのRuby bindingを書いていて、やっぱりGC周りではまってしまった。

具体的には、オブジェクトの開放の順番を指定しないといけないのだが、 Rubyではfinalizerの実行順は不定だし、先に消えてほしいオブジェクトからリファレンスを持ったりしてみたのだが、 rootからの参照を一度に消されるとどうしようもない。

そこで試しに以下のようなコードを書いてみたところうまくいった。

module OrderedFinalizer
  @@queue = []

  def self.add(*v)
    @@queue << weaken_reference(v)
  end

  def self.weaken_reference(v)
    v.last.map!(&:object_id)
    v
  end

  def self.step
    @@queue.each do |group|
      unless group.last.any?{|e|ObjectSpace._id2ref(e) rescue nil}
        group.pop
        weaken_reference(group) unless group.empty?
      end
    end
    @@queue.reject!(&:empty?)
    @@queue.empty?
  end

  at_exit do
    GC.start until self.step
  end
end


# obj2, obj3 ☞ obj1 という順番で解放したい
OrderedFinalizer.add [obj1], [obj2, obj3]
OrderedFinalizer.add [obj1], [obj4, obj5]

以下軽く解説。

  1. OrderedFinalizerに解放順序を指定したいオブジェクトへのリファレンスを保持させる。
  2. 一番先に解放してほしいオブジェクトだけはweak reference(つまりobject_id)で持つ。
  3. weak referenceなオブジェクトが解放されていたら、次のオブジェクトの参照をweakにする。

OrderedFinalizerはat_exitで参照されているので、Ruby界では最後まで生き残る。

stepを各GC毎に呼ぶのが理想なのだが、そんなイベントはないよなぁ。 今回の用途には足るんだけど、今のままだと、最後までいくらかのオブジェクトを保持してしまう。

当然、obj1からobj2へのリファレンスがあったりすると無限ループになるし、[true]とか渡してやるとかも困る。

あるいは別の解法求む。