mrruby-sqlite
FreeBSD upgrade
root@fb:~ # freebsd-update upgrade -r 13.1-RELEASE : The following components of FreeBSD seem to be installed: kernel/generic kernel/generic-dbg src/src world/base The following components of FreeBSD do not seem to be installed: world/base-dbg Does this look reasonable (y/n)? y Fetching metadata signature for 13.1-RELEASE from update2.freebsd.org... done. : marge some file by hand root@fb:~ # freebsd-update install Kernel updates have been installed. Please reboot and run "/usr/sbin/freebsd-update install" again to finish installing updates. shutdown -r now
root@fb:~ # freebsd-update install Installing updates...Scanning //usr/share/certs/blacklisted for certificates... Scanning //usr/share/certs/trusted for certificates... Scanning //usr/local/share/certs for certificates... done. root@fb:~ # freebsd-update install No updates are available to install. Run '/usr/sbin/freebsd-update fetch' first. shutdown -r now
コンパイルの準備
前回Rakefileをみた時、いきなりモジュールMRubyが使われていてびっくりした。どんなタイミングでbuild.rb等のMRuby構成要素を読込みしてるのだろう?
こういう時は、トレースするに限るな。
sakae@deb:/tmp/mruby$ strace -o SLOG rakae
まずはRakefileがオープンされて、それからbuild.rbがオープンされてる。
sakae@deb:/tmp/mruby$ egrep '(Rakefile|build.rb)' SLOG | grep -v 'No such' | grep open openat(AT_FDCWD, "/tmp/mruby/Rakefile", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5 openat(AT_FDCWD, "/tmp/mruby/Rakefile", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5 openat(AT_FDCWD, "/tmp/mruby/lib/mruby/build.rb", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5 openat(AT_FDCWD, "/tmp/mruby/lib/mruby/build.rb", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5
他の構成要素はどうだ? なるべくユニークなファイル名を指定してあげる。
sakae@deb:/tmp/mruby$ egrep '(presym.rb|build.rb)' SLOG | grep -v 'No such' | grep open openat(AT_FDCWD, "/tmp/mruby/lib/mruby/build.rb", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5 openat(AT_FDCWD, "/tmp/mruby/lib/mruby/build.rb", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5 openat(AT_FDCWD, "/tmp/mruby/lib/mruby/presym.rb", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5 openat(AT_FDCWD, "/tmp/mruby/lib/mruby/presym.rb", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5
やっぱり、大本のRakefileを見るのが基本。
MRUBY_ROOT = File.dirname(File.expand_path(__FILE__)) $LOAD_PATH << File.join(MRUBY_ROOT, "lib") # load build systems require "mruby/core_ext" require "mruby/build"
冒頭はこんな風に始まっている。そしてオイラーは冒頭で躓いた。
sakae@deb:/tmp/mruby$ irb irb(main):001:0> File.dirname(File.expand_path(__FILE__)) => "/tmp/mruby" sakae@deb:/tmp/mruby$ cd src/ sakae@deb:/tmp/mruby/src$ irb irb(main):001:0> File.dirname(File.expand_path(__FILE__)) => "/tmp/mruby/src"
なる程、pwdのruby表現なのね。で、得られたdirにlibを結合して、それををロードパスに追加する。後は、requireしてるのね。なる程。読込みがまだまだ、浅いな。
racc
mrubyのコンパイルログをみていると、rubyのコードをコンパイルする装置を用意してる。どうやら、これがそうみたい。
vbox$ ls /tmp/mruby/mrbgems/mruby-compiler core/ mrbgem.rake p/mruby/mrbgems/mruby-compiler/core/ < codegen.c keywords lex.def node.h parse.y y.tab.c
binsonとかで、ごにょごにょするやつ。難しいみたいなので、rubyの同等品を当ってみる。
Racc で遊ぼう なにか、昔流行した256倍シリーズの本を彷彿させるな。 ちょいと遊んでみたよ。こういう道草も楽しいな。
mruby-sqlite
mrubyのHPにmruby用のgemの集積が案内されてる。つらつら見ていたら、面白そうなものを発見した。 mruby-sqlite
これはもう試してみるしか。buildの案内はこうなってた。
Download or clone MRuby Add the mruby-sqlite gem declaration to MRuby's build_config.rb file. Use conf.gem :github => 'jbreeden/mruby-sqlite' to have MRuby checkout the lasest version automatically before building. Use conf.gem 'PATH/TO/mruby-sqlite' if you've already downloaded this repo. Run rake in MRuby's root directory.
build_config.rb
に設定をしろって亊だけど、これは古い。現代のmrubyでは、こちらに設定を行う。あらかじめ、塊をDLしておいた。
[sakae@fb /tmp/mruby]$ cat build_config/default.rb : # include the GEM box conf.gembox 'default' conf.gem '/tmp/mruby/mruby-sqlite'
そして、mrubyを最初からコンパイルする。後で設定を付け足しでもダメ。一気に組込むのがmrubyの流儀。rake clean; rake てやるのが正解。
[sakae@fb /tmp/mruby]$ rake : LD build/host/bin/mirb ld: error: undefined symbol: pthread_create >>> referenced by sqlite3.c:22701 (mruby-sqlite/src/sqlite3.c:22701) >>> sqlite3.o:(sqlite3ThreadCreate) in archive /tmp/mruby/build/host/lib/libmruby.a clang: error: linker command failed with exit code 1 (use -v to see invocation) rake aborted! Command failed with status (1): [clang -o "/tmp/mruby/build/host/bin/mirb"...] /tmp/mruby/lib/mruby/build/command.rb:37:in `_run' /tmp/mruby/lib/mruby/build/command.rb:217:in `run' /tmp/mruby/tasks/bin.rake:17:in `block (4 levels) in <top (required)>' /tmp/mruby/Rakefile:42:in `block in <top (required)>' Tasks: TOP => build => /tmp/mruby/bin/mirb => /tmp/mruby/build/host/bin/mirb (See full trace by running task with --trace)
んが、エラーだよ。追加したsqliteで名前解決出来無いとな。でも、よくみるとスレッドが作る方法が不明だよのエラーだ。
野生の勘を働かせて、 mruby-thread も一緒にコンパイル。これで、上手くいった。本当に使えるか実験。
sql.rb 2022-04-17(App by webrick) からコピペ。但し、require 'sqlite3' は不要。mrubyでは、既に組込まれているから。
[sakae@fb /tmp/mruby]$ cat sql.rb def search(sw) ew = sw + "%" db = SQLite3::Database.new("/usr/share/dict/ejdict.sqlite3") stmt = db.prepare("SELECT word,mean FROM items WHERE word like ? limit 3") stmt.bind_param(1, ew) stmt.execute().each do |res| puts("<dt>#{res[0]}</dt>\n") puts("<dd>#{res[1]}</dd>\n") end stmt.close(); db.close() end puts search("ruby")
[sakae@fb /tmp/mruby]$ bin/mruby sql.rb <dt>ruby</dt> <dd>〈C〉紅玉,ルビー / 〈U〉ルビー色,深紅色(しんこうしょく) / ルビー色の,深紅の, 本発のプログラミング言語</dd> true
やったね!!
mirbでも当然requireは使えないし、頼みの綱のloadも無い。上の場合だと、関数をコピペで入力してあげよう。
尚、多数のgemが公開されてるけど、みんな5年以上前のものばかりだ。ひょっとしたら現代のmrubyで動かないものもあるかも知れない。
OpenBSDでも、同じ亊をやってみた。ええ、ちょっと確認するのを忘れていたので。
vbox$ ldd bin/mruby bin/mruby: Start End Type Open Ref GrpRef Name 163ca000 363da000 exe 2 0 0 bin/mruby 05ef5000 25ef7000 rlib 0 1 0 /usr/lib/libm.so.10.1 0dfe1000 2dfe3000 rlib 0 1 0 /usr/lib/libpthread.so.26.1 05bc4000 25bd5000 rlib 0 1 0 /usr/lib/libc.so.96.1 0a6d2000 0a6d2000 ld.so 0 1 0 /usr/libexec/ld.so
sqlite3は内部に格納してるけど、pthreadはシステムのそれを借用してるのね。まあ、sqlite3は組み込みを目指しているから、組み込んでしまえって亊なんでしょうね。
それから、sql.rbは、関数なんだけど、括弧が省けるのか? これも疑問なんで確認。
vbox$ bin/mirb mirb - Embeddable Interactive Ruby Shell > def search(sw) * ew = sw + "%" * db = SQLite3::Database.new("/usr/share/dict/ejdict.sqlite3") : * stmt.close(); db.close() * end => :search > search 'ruby' <dt>ruby</dt> <dd>〈C〉紅玉,ルビー / 〈U〉ルビー色,深紅色(しんこうしょく) / ルビー色の,深紅の, 本発のプログラミング言語</dd> => true
on debian
ついでなので、毛色の違う、リナ系でもやってみる。
sakae@deb:/tmp/mruby$ rake : LD build/host/bin/mirb /usr/bin/ld: /tmp/mruby/build/host/lib/libmruby.a(sqlite3.o): in function `unixDlOpen': /tmp/mruby/mruby-sqlite/src/sqlite3.c:31507: undefined reference to `dlopen' :
こいつも、問題児だな。ダイナミックロードは、ほぼ必須だと言うのに、デフォの設定に入っていない。いや、それは違うだろう。組み込み系なんで、リナだけ特別扱いはしませんと言う、大人の配慮がなされている。
tasks/toolchains/gcc.rake に、デフォを増やす設定を追加。
linker.libraries = %w(m dl)
これで、ちゃんと、辞書アプリも動くようになった。
sakae@deb:/tmp/mruby$ ldd bin/mirb linux-gate.so.1 (0xb7f8a000) libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb7c93000) libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xb7c8d000) libreadline.so.8 => /lib/i386-linux-gnu/libreadline.so.8 (0xb7c3a000) libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb7c18000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7a2f000) /lib/ld-linux.so.2 (0xb7f8c000) libtinfo.so.6 => /lib/i386-linux-gnu/libtinfo.so.6 (0xb7a06000)
libdlが組込まれている。mirbの場合は、追加でlibreadlineも加わるのか。
mruby-threadを組み込みしたけど、linker.libraries の所に、pthreadを追加した方がよさげだな。
(m)ruby with gnuplot
上でraccを調べていた時、プログラムで遊ぼうというサイトに行き着いたんだ。面白いやつがあったので、ちょっとやってみる。
題材は、奥村晴彦著「C 言語による最新アルゴリズム事典」 から、風が吹けば桶屋が儲かるじゃなくて、アマゾンで蝶が羽搏けば、日本が大嵐になる(かもしれない)という有名なやつ。
なんてのは、表の説明。気象予報士の試験に出る、何故、天気予報は当らないを、素人に説明するためのやつです。
大気循環モデル。どんな風に大気が動くか、時間を追って計算。桜の花弁なり枯葉が、風に舞ってどう動くかを説明する式。花弁の位置は、3次元の座標 x,y,z で表わす、結果になる。
A,B,Cは、気温の傾斜だとか、気圧の傾きだとか、要する風の発生原因を表すパラメータだ。 花弁は、初期状態の位置、x=y=z=1の原点にある。そこから、細かい時間区切Dによって、少しづつ、系を更新してくって亊で、計算を進行させる。
やってみると分るけど、パラメータがちょっと違っただけで、とんでもなく異った結果になる。 いかにパラメータをちゃんと計測しても、何らかの誤差が内在してる。そのため、結果がバラケル亊になるよって亊だ。こればかりは、どんなスパコンをもってしても、解決は出来無い。
/*********************************************************** lorenz.c -- Lorenz (ローレンツ) アトラクタ ***********************************************************/ #include "plotter.c" /* ラージモデルでコンパイル */ #define A 10.0 #define B 28.0 #define C (8.0 / 3.0) #define D 0.01 int main() { int k; double x, y, z, dx, dy, dz; gr_on(); gr_window(-30, 0, 30, 60, 1, GREEN); x = y = z = 1; for (k = 0; k < 3000; k++) { dx = A * (y - x); dy = x * (B - z) - y; dz = x * y - C * z; x += D * dx; y += D * dy; z += D * dz; if (k > 100) draw(x, z); else move(x, z); } hitanykey(); return EXIT_SUCCESS; }
これをruby語に翻訳する。翻訳なんておこがましい。ちょっと変換しただけだ。知らない間に、微分方程式の数値解を求めてる。pythonでAIは、うんざりだからね。
require "plotter" A = 10.0 B = 28.0 C = (8.0 / 3.0) D = 0.01 def main() gr_on() x = y = z = 1 for k in 0..3000 do dx = A * (y - x) dy = x * (B - z) - y dz = x * y - C * z x += D * dx; y += D * dy; z += D * dz if k > 100 then draw(x, z) else move(x, z) end end gr_off() end main()
ドライバーは、gnuplotの駆動ルーチンだ。 /usr/local/lib/ruby/site_ruby/
とかに入れておくと、寂る亊なく未来永劫で使えるぞ。
それから、翻訳の過程で、 gr_window( ... )
を省略しちゃった。これは、X、Y軸の範囲を固定するもの。gnuplotは、範囲指定がないと、自動で決定してくれる。
# coding: utf-8 #/*********************************************************** # plotter.rb -- グラフィックス #***********************************************************/ # プロッタのシミュレーション $xpen = 0; $ypen = 0; # ペンの現在位置 def gr_on $gplot = IO.popen("gnuplot -persist", "w") $gplot.puts 'plot "-" w l' end def gr_off $gplot.puts "end" $gplot.close end def gr_wline(x1, y1, x2, y2) $gplot.puts "#{x1} #{y1}\n#{x2} #{y2}\n\n" end def move(x, y) # ペンアップで移動 $xpen = x; $ypen = y end def move_rel( dx, dy) # 同上 (相対座標) $xpen += dx; $ypen += dy end def draw(x, y) # ペンダウンで移動 gr_wline($xpen, $ypen, x, y) $xpen = x; $ypen = y end def draw_rel( dx, dy) # 同上 (相対座標) gr_wline($xpen, $ypen, $xpen + dx, $ypen + dy) $xpen += dx; $ypen += dy end # sample usage =begin gr_on gr_wline(0, 0, 10, 10) move(5, 0) draw_rel(0, 5) gr_off =end
mirbに最初、plotter.rbを貼り付けてから、次に本体を貼り付けたら、ちゃんとグラフが出てきた。素晴しい。
vbox$ bin/mirb : > def draw_rel( dx, dy) # 同上 (相対座標) * gr_wline($xpen, $ypen, $xpen + dx, $ypen + dy) * $xpen += dx; $ypen += dy * end => :draw_rel > gr_on => nil > for x in 0 .. 100 do * draw x, Math.sqrt(x) * end => 0..100 > gr_off => nil
こんな風に、貼り付けてから、手動でグラフを描画しても、勿論OK。matplotだかの使いかたを思い出すより、よっぽど楽です。
plotter 改造
上で使ったのは、プロッターをシュミレーションする技術。勿論デバイスは、gnuplotだ。
でも、今や2次元平面を描画するだけじゃなくて、3Dが大流行。そう3Dプリンターね。 宇宙へ持っていって、そこで工作しましょって時代。 宇宙空間において3Dプリンターで人工衛星アンテナを製造する技術を開発
地上にいる我々も、3Dプロッターしたい。 そんな目論見もあって、あえて基礎になるコードを展開させてもらってる。作者様には感謝の念がたえません。
デバイス本体が対応してるか? 答はYES。protってなってる所を、splotに変更するのがまずは必要。後は、x, y とかしてる所を、x, y, z に変更するだけ。おっと、$zpen = 0 も忘れないでね。機械的な変更だから、容易に出来るだろう。
2Dと3Dを同居させるなら、draw(x,y) draw3(x,y,z) ぐらいで片づけるのいいかな。
gr_on
の所は、 gr3_on
ね。'splot "-" w l' 3Dでプロットしてね。"-" は、データは、パイプからの奴を使ってね。w l は省略形。正式に書くと、with line 線グラフって亊。
パイプからのデータ流し込みが終ったら、end で、教えてあげる。これは、2Dも3Dも一緒だ。 共通に使える。