もっと ruby

観光用に byebug を便利に使う

前回使ったbyebugって言うruby用のdebuggerが非常に良く出来ているので、emacsから使えないか調べてみた。

Module to add byebug debugger support to emacs realgud.

なんか、便利そう。でも待てよ。BPを貼って、breakpointにヒットした時、お約束でbtは行うな。でも、続いてフレームを上り下りするのは、やっていなかった。ちょいと確認してみるか。

frameをup/downせよ

題材は前回登場したminps.rbです。

(byebug) up

[966, 975] in /tmp/minips.rb
   966:   }
   967:   define_op("moveto"){|vm|
   968:     y = vm.pop_op(:numeric).value
   969:     x = vm.pop_op(:numeric).value
   970:     vm.set_point(Vector[x, y])
=> 971:     vm.add_path(:moveto, vm.real_point)
   972:   }
   973:   define_op("rmoveto"){|vm|
   974:     y = vm.pop_op(:numeric).value
   975:     x = vm.pop_op(:numeric).value
(byebug) bt
    #0  MiniPS::VM.transform_point(v#Vector) at /tmp/minips.rb:1325
    #1  MiniPS::VM.real_point at /tmp/minips.rb:1329
--> #2  block in block in <module:MiniPS> at /tmp/minips.rb:971
    :
(byebug) f
--> #2  block in block in <module:MiniPS> at /tmp/minips.rb:971
(byebug) f 9

[1456, 1465] in /tmp/minips.rb
   1456:       end
   1457:       print vm.ostack.empty? ? ">" : "[#{vm.ostack.size}]>"
    :

up/gdownで10行分表示してくれるのか。rubyの関数は比較的小ぶりに書かれる事が多いので、視認するにはこれで十分な気がする。

今居るフレームは、引数無しのfで確認出来る。引数を与えると、指定したフレーム番号の所を表示してくれる。

普通にlすれば、先に進むし、l- すれば、逆に前に戻るようになる。l= で、元の場所に戻る。なかなか考えたインターフェースになってるな。これなら、わざわざemacsの手を煩わせる必要な無いな。

デフォルトだと10行分を表示するけど、下記のようにすると、20行分を表示するようになる。 長いメソッドは警告の対象にする(by matz)からねってのを、思い出せ。こういう処置をしないと満足に閲覧出来ないコードは、怪しいぞと思っていれば、間違いない(個人の独断と偏見です)。

(byebug) set listsize 20
Number of source lines to list is 20

毎回入力するのが面倒なら、.byebugrc に書いておけばいいのだな。

それから、もっと過激?に、BPに達した時等で、listの表示が必要無いなら、

set noautolist

を設定しておけば良い。何時でも、lで表示出来るからね。

trace で楽チン追跡

つらつらと実技書を見てたんだ。そしたらステップ実行を自動化する方法が指南されてた。前回は、byebugのオプション -t が使えねぇと嘆いていたからね。ちゃんと説明書は読みましょうの、良い事例だな。折角作者様がインプリメントしているのだから、使わないと失礼にあたるってもんです。

(byebug) b 1325
Created breakpoint 1 at /tmp/minips.rb:1325
(byebug) c
minips>1 2 moveto
Stopped by breakpoint 1 at /tmp/minips.rb:1325
  :
   1328:     def real_point
   1329:       transform_point @gs.point
(byebug) set linetrace
linetrace is on
(byebug) set basename
basename is on
(byebug) c
Tracing: matrix.rb:2006     @elements[i]
Tracing: matrix.rb:2006     @elements[i]
Tracing: matrix.rb:1953     new convert_to_array(array, false)
Tracing: matrix.rb:1720       case obj
Tracing: matrix.rb:1722         copy ? obj.dup : obj
Tracing: matrix.rb:1993     @elements = array
Tracing: minips.rb:1333       raise "no current point" if !drawing?
Tracing: minips.rb:1272       !@gs.point.nil?
Tracing: minips.rb:1334       @gs.current_path << [com, *args]
Tracing: minips.rb:371       true
Tracing: minips.rb:1462         buf = ""
Tracing: minips.rb:1463         cont = false
Tracing: minips.rb:1452       if !cont
Tracing: minips.rb:1453         print "minips"
minipsTracing: minips.rb:1457       print vm.ostack.empty? ? ">" : "[#{vm.ostack.size}]>"
>Tracing: minips.rb:1458       l = $stdin.gets

上記のように、一度breakさせる。そしてlinetraceをセット、絶対Pathでのファイル名表示を抑制、そして継続。余す所なくトレースしてくれた。

私の、 .byebugrc

ob$ cat .byebugrc
set listsize 20
# set noautolist
set basename

わざわざ載せる程の事も無いけど、参考例と言う事で。最初、コメントを外して、余計な表示は避けていたんだけど、気が変わった。常に20行表示させるようにした。表示をけちってもしょうがないからね。

TkSimpleClassBrowser

『オブジェクト指向スクリプト言語Ruby』と言う20年前に出てた本の例を前回探して、右往左往した。今回は、きちんと探してみたよ。

TkSimpleClassBrowser

そしたら、懐かしいページが出て来た。あの頃は、このHPに非常にお世話になったよ。まだ継続ってか公開されてるのに、驚きを禁じ得ない。

ここで公開されてるTkのアプリが、無修正で動いた。息が長いなあ。感心しますよ。gtkとかだと、そうは問屋が卸しませんよ。安心して使っていいぞ。

svg

これまた、前回登場したSVGです。知らない事は恥じゃ無い。調べてみれば良いのですから。

SVG: Scalable Vector Graphics

真っ先に、モジラ組に案内された。残念ながら、良く分かった人用のページだな。下記を見て予習してから、再訪せよ。

スタートアップ SVG

ちょい昔の資料。って事は、息が長いって事だな。これで分かった(かな?)

SVG by C

毎度お世話になってる先生のページ。世界標準語のC語で書かれた例が載ってるから、分かり易い。

SVGを編集するフリーで使えるソフトとしては、 InkScape が有名らしい。興味を持って試してみるか。

変換関数 / Matrix 再びモジラ組に戻って、うろうろしてたら、アフィン変換が出て来た。もう、恐くないよ。 グラフィックをやってる限り、何処にでも顔を出す、お馴染みさんですから。

inkscape

忘れないうちに、inkscapeを試してみる。入れたのはdebian機。グラフィックのカテゴリーに起動メニューが出て来た。inkscape-tutorialsも入れるといいよって事なので入れた。 /usr/share/inkscape/examples/に山ほどサンプルが有ったぞ。

大好物のcar.svgなんてのが入っていた。残念ながら、cdr.svgとかcons.svgは無かった。でも、tiger.svgzは入っていた。zが付いているのは、

debian:~$ file /usr/share/inkscape/examples/tiger.svgz
/usr/share/inkscape/examples/tiger.svgz: gzip compressed data, was "tiger.svg", last modified: Thu Feb  3 08:55:08 2005, from Unix, original size 141878

サイズが大きいから、圧縮しておきましたって奴だ。そう言えば、最近プチ話題になった、 プロゴルファーのタイガー・ウッズ氏が23日、ロサンゼルスで自動車事故を起こして両足を複雑骨折 が有ったね。圧縮されて死ななくてよかったね。

起動してみると、画面の4辺に山ほど、ツールボタンが並んでいる。どうやって使うものやら? 早速の拒否反応と言うか、副反応が発現しそう。そう、コロナ繋がりで、最近良く聞く言葉で、副反応ってのが有るけど、これって副作用って言うネガティブな言葉を、やんわりさせて、恐怖心を抑えているのかな。

debian:inkscape$ ls tutorials/*ja*svg
tutorials/tutorial-advanced.ja.svg     tutorials/tutorial-interpolate.ja.svg
tutorials/tutorial-basic.ja.svg        tutorials/tutorial-shapes.ja.svg
tutorials/tutorial-calligraphy.ja.svg  tutorials/tutorial-tips.ja.svg
tutorials/tutorial-elements.ja.svg     tutorials/tutorial-tracing.ja.svg

日本語の指南書も有るから、ほんわかとやってみて、恐怖心を克服すればいいんだな。ちょい見したら、図形を重ねあわせたら、下の層が透けて見えるような効果も発動出来るみたいだ。

勿論CUIからも、使えるとな。

Export an SVG file into PNG with the default resolution of 96 dpi (one
SVG user unit translates to one bitmap pixel):

    inkscape filename.svg --export-png=filename.png

Same, but force the PNG file to be 600x400 pixels:

    inkscape filename.svg --export-png=filename.png -w600 -h400

svgファイルをチラ見すると、

    <path
       style="fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:16;stro\
ke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M -147.17109,-1087.1156 C -153.74248,-1087.7954 -159.61044, ...

こんな風に、座標が浮動小数で表現されてた。これが当たり前の事なのか。

lambda

ここまで、前回の復習と言うかフォローだった。ここからは純粋Rubyについて、また少し調べてみる。手始めは、

Rubyのアロー演算子を用いたラムダ計算の基礎表現

で、出たーと言ったらお化け。どこにも存在するやつです。勿論rubyにも有ります。 こういう記号って、なかなか調べつらいんだよな。そんな時は、躊躇なく、 Rubyで使われる記号の意味(正規表現の複雑な記号は除く) こういう所を見るに限る。ついでに言うと、正規表現自慢は止めよう。

"a is #{a}"

    リテラル/式展開

      a = 10
      p "a is #{a}"  #=> "a is 10"

gauche用語では、インターポーレーションとか言っってたな。良く出て来る便利な記法だ。

irb(main):001:0> 3.times {|x| puts x}
0
1
2
=> 3

{ … } って、rubyでは、どこにも出て来るやつ。空気のように使ってます。

(lambda (x) (puts x))

って関数に、次々に値を渡せば、上記のrubyになりますな。pythonだと無骨にlambdaが有るけど、rubyだとカッコよくhaskellもどきに、

irb(main):001:0> ->(x){ puts x }[123]
123
=> nil

と、書くわけだな。

square = Proc.new {|x| x**2 }

square.call(3)  #=> 9
# shorthands:
square.(3)      #=> 9
square[3]       #=> 9

こんなのが、Procに出てたぞ。ちゃんと名前を付けてあげましょうと言う例だな。

なんでもλ

あっ、λって、lambdaの事ね。こう見てくると、rubyってのは、matz lisp って事がよく分かる。

関数型プログラミング

Rubyで関数型プログラミング#1: ステート

Rubyで関数型プログラミング#2: クロージャ

Ruby: Enumerableをreduceで徹底理解する#1 基本編(翻訳)

sakae@pen:/tmp$ ri Enumerable
= Enumerable

(from ruby core)
------------------------------------------------------------------------
The Enumerable mixin provides collection classes with several traversal
and searching methods, and with the ability to sort. The class must
provide a method #each, which yields successive members of the
collection. If Enumerable#max, #min, or #sort is used, the objects in
the collection must also implement a meaningful <=> operator, as these
methods rely on an ordering between members of the collection.

------------------------------------------------------------------------
= Instance methods:

  all?, any?, chain, chunk, chunk_while, collect, collect_concat, count,
  cycle, detect, drop, drop_while, each_cons, each_entry, each_slice,
  each_with_index, each_with_object, entries, filter, filter_map, find,
  find_all, find_index, first, flat_map, grep, grep_v, group_by,
  include?, inject, lazy, map, max, max_by, member?, min, min_by,
  minmax, minmax_by, none?, one?, partition, reduce, reject,
  reverse_each, select, slice_after, slice_before, slice_when, sort,
  sort_by, sum, take, take_while, tally, to_a, to_h, to_set, uniq, zip

関数型と相性が良いのは、上記です。lambdaで定義したブロックと言うか無名関数に、効率よく値を渡す、かつ何等かの機能を盛り込むのが非常に楽。

irb(main):006:0> (1..10).each_cons(8) { |a| p a }
[1, 2, 3, 4, 5, 6, 7, 8]
[2, 3, 4, 5, 6, 7, 8, 9]
[3, 4, 5, 6, 7, 8, 9, 10]
=> nil

何やら、オイラーの大好物であるconsが出てきたので、試してみた。こんなの知るかって動きだな。数学の行列を研究してる人かたの要望だろうか?

他にも、試してみたいメソッドが盛り沢山だな。

ruby/gnuplot

これからお世話になるかも知れないgnuplotのI/Fを入れておこう。人気度は、何も接頭辞がつかない奴だった。これぞmatzさんご推薦のシンプルは素晴らしいって奴だな。

オイラーは、ひねくれものなんで、暴君のお勧めに従ってみる。

Numo::Gnuplot : Gnuplot interface for Ruby

サンプルに有ったものを試してみる。 ex006.rb

require "numo/gnuplot"
# X-Y data plot from file
fn = "tmp.dat"
open(fn,"w") do |f|
  100.times do |i|
    x = i*0.1
    f.printf("%g %g\n", x, Math.sin(x))
  end
end

Numo.gnuplot do
  set title:"X-Y data plot from file"
  # if 'using' option is given,
  # the first string argument is regarded as a data file.
  plot fn, using:[1,2], w:'lines', t:'sin(x)'
end

こういう、書きっぱなしのコードを試すには、コピペしてみるか、馬鹿と鋏は使いようモードに突入して、requireをハサミの如く使うに限る。

debian:examples$ irb
irb(main):001:0> require './ex006'
=> true

libraryのPathは、普通wdirが登録されていないので、./hoge ってやるのが味噌。

tmp.datって言う実体ファイルが作成されるんだけど、メイン側(?)では、そんな下々の話が全く出てこないってのが、現代風なんですかね? いや、ちょいと誤解してた。fnなんていかにも関数でございますなんて名前なんで、新種のI/Fかと思っちゃったぞ。普通のよくあるパターンでした。

現代風と言えば、pythonのキラーライブラリィーであるnumpyに似せたruby仕様の奴が、Muno.gnuplotからも使えるようになっている。numo/narray ですって。深入りしそうなので、それは止めておく。

require "numo/gnuplot"
x = ['a','b','c','d','e']
y = [10,20,40,30,45]

Numo.gnuplot do
  set title:'Bar chart with text labels'
  set boxwidth:0.5
  set 'grid'
  set yrange:[0..50]
  set style:[fill:'solid']
  plot x,y, using:[2, 'xtic(1)'], w:'boxes', t:'series'
end

これ棒グラフの例だけど、変形したら、色々と使い道が有りそう。


This year's Index

Home