emacs buffer

Table of Contents

benchmark-run

前回、ベンチマークなんてのが用意されてるのを知ったものだから、試してみ る(gaucheならtimeなんだけどね)。試験台は、迷路を作成する関数(から、結果の表示をしないようにしたも の)。

ELISP> (benchmark-run (generate 10 30))
(0.078006929 0 0.0)

ELISP> (benchmark-run 10 (generate 10 30))
(0.863384621 1 0.045810978)

実行時間、GCの回数、GCの時間ってとこかな。早起きは三文の得に習って、ソー スに対面しとく。これぞOSSの最大の楽しみだからね。

(defmacro benchmark-run (&optional repetitions &rest forms)
  "Time execution of FORMS.
If REPETITIONS is supplied as a number, run FORMS that many times,
accounting for the overhead of the resulting loop.  Otherwise run
FORMS once.
Return a list of the total elapsed time for execution, the number of
garbage collections that ran, and the time taken by garbage collection.
See also `benchmark-run-compiled'."
  (declare (indent 1) (debug t))
  (unless (or (natnump repetitions) (and repetitions (symbolp repetitions)))
    (setq forms (cons repetitions forms)
          repetitions 1))
  `(benchmark-call (lambda () ,@forms) ,repetitions))

マクロ仕様の関数になってる。して、その核心は、

(defmacro benchmark-elapse (&rest forms)
  "Return the time in seconds elapsed for execution of FORMS."
  (declare (indent 0) (debug t))
  (let ((t1 (make-symbol "t1")))
    `(let ((,t1 (current-time)))
       ,@forms
       (float-time (time-since ,t1)))))

時間計測の方法が分ったぞ。実際に分解してやってみる。

ELISP> (defvar t1 (current-time))
t1
ELISP> (float-time (time-since t1))
9.40873745
ELISP> t1
(25879 21730 73284 817000)

current-timeで現在時刻を記憶。time-sinceで、そこから(t1)の時間差を算出。 後は、それを人間様用に変換してるんだな。

やや、ただのbenchmarkってのも有るな。こちらは、ただの関数だ。

ELISP> (benchmark 20 (generate 10 30))
"Elapsed time: 0.000004s"
ELISP> (benchmark 20 '(generate 10 30))
"Elapsed time: 1.701876s (0.046847s in 1 GCs)"

だから評価したい関数には、先頭に ' を付けて、データだから、ここでは評価しないで ねって指示が必要。

ついでなので、current-timeの源流まで、遡ってみる。

make_lisp_time (struct timespec t)
{
  if (CURRENT_TIME_LIST)
    {
      time_t s = t.tv_sec;
      int ns = t.tv_nsec;
      return list4 (hi_time (s), lo_time (s),
                    make_fixnum (ns / 1000), make_fixnum (ns % 1000 * 1000));
    }

t.tv_sec が2つに分解されてるのは、整数のビット数が、30ビットのマシ ンを救済するためみたいだ。おかげで貧乏人でも快適にemacsを利用できる。 どこかのリナとかパイソンみたいに、切り捨てはしないのさ。

まだ上流が有るけど、これぐらいにしておく。1970-01-01 00:00:00 からの経 過時間って事で、察してください。

with buffer

前回からの宿題、迷路を作成して探索経路も一遍に表示したい。普通に考えた ら、スクリプトにしちゃえば良いはずだけど、それじゃ負けた気分。 で、emacsのバッファーとやらの登場。

editorである限りファイルを編集できなければならない。それには、ファイル の内容を一旦メモリーに読み込んでとなる。そのメモリーの事をバッファーと 呼んでいる。

下記の簡単なバッファーの使用例をあげる。

;; test-bufferという名前のバッファを作成し、いろいろな内容を書き出す
(with-current-buffer (get-buffer-create "test-buffer")
  ;; バッファの内容を空にする
  (erase-buffer)
  ;; /tmp/foo.txtの内容を挿入する
  (insert-file-contents "/tmp/foo.txt")
  ;; 文字列を挿入する
  (insert "End\n")
  ;; バッファの内容をファイルに書き出す
  (write-region (point-min) (point-max) "/tmp/bar.txt"))

で、出来たのが下記。バッファーを作成して、その中にprint-maze似のpmb関 数を利用して出力。後はそのバッファーをLOGファイルに書き出す。一番簡単な奴 だな。

(defun qa ()
  (setq maze (new-maze 12 30))
  (dig-maze maze 1 1)
  (with-current-buffer (get-buffer-create "test-buffer")
    (erase-buffer)
    (pmb maze)
    (write-region (point-min) (point-max) "/tmp/LOG"))
  (testr)
  (print-maze rmaze res) )

肝心のpmb関数だけど、princをinsertに変更。terpriはnewlineに変更したも のだ。いわゆるlisp系の出力と、emacsのbuffer系の出力では、系統が違うの ね。間を取り持つ何かが有ってもよさそうだけど、今の所探し当てられていな い。果して、そんなのあるんか?

同じロジックで中身がちょっと違うってのは、まずい。非常にまずい。Railsの人達か ら、ヤーイ、DRYの原則に違反してるって指摘されるぞ。さて、どうする? マクロの出番かな? まて、マクロは安易に使うな、と、マクロの指南書 On Lispに書いてあったぞ。

print-maze と pmb の 融合

マクロを使わないで、2つの関数を融合せよ。散歩してたらアイデアが閃いた。 切り替え用の引数を追加する。文字列を直接出力すんじゃなくて、一旦、文字 列に貯めると言う方法。そして、改行の場面で、出力先を振り分け。

format

まずは振り分けの手法。formatなんてのを思い出した。ちょいとsbclで確認。

 * (format t "hello")
hello
NIL
 * (format nil "hello")
"hello"

おまけで、gaucheでも確認。

gosh$ (format #t "hello")
hello#<undef>
gosh$ (format #f "hello")
"hello"

formatの主目的は、printfのように、自在にフォーマッティグする事なんだけ ど、第一引数により、結果を端末に送るか、文字列にするか指定できるんだ。

なおemacsにも同名の関数が用意されてるけど、切り替え機能は無い。 4.7 Formatting Strings on emacs

文字列の連結

探し方が悪いのか、特別な関数は用意されてないっぽいんで、下記で我慢かな。

ELISP> (defvar s "")
s
ELISP> (setq s (concat s "hello "))
"hello "
ELISP> (setq s (concat s "world."))
"hello world."

ついでに、関数定義も

ELISP> ((lambda (x) (princ x)(terpri)) s)
hello world.

t

join print-maze and pmb

融合させてみた。

(defun pmb (term maze &optional marks)  ; if term is nil then output to buffer
  (defun toterm (s) (princ s) (terpri))
  (defun tobuf  (s) (insert s) (newline))
  (let (s)
    (dotimes (r (1- (maze-rows maze)))
    
      (setq s "")
      (dotimes (c (1- (maze-cols maze)))
        (setq s (concat s (if (maze-is-set maze r c 'ceiling) "+---" "+   "))))
      (setq s (concat s "+"))
      (if term (toterm s) (tobuf s))
      
      (setq s "")
      (dotimes (c (1- (maze-cols maze)))
        (setq s (concat s (if (maze-is-set maze r c 'wall) "|" " ")))
        (setq s (concat s (if (member (cons r c) marks) " * " "   "))))
      (setq s (concat s "|"))
      (if term (toterm s) (tobuf s)))
      
    (setq s "")
    (dotimes (c (1- (maze-cols maze)))
      (setq s (concat s (if (maze-is-set maze (1- (maze-rows maze)) c 'ceiling) "+---" "+   "))))
    (setq s (concat s  "+"))
    (if term (toterm s) (tobuf s))))

let節の中に、totermとかを突っ込みたかったけど、それは許されなかった。

実行例

ELISP> (qa 2 3)
+   +---+---+
| *   *     |
+   +   +---+
|   | *   * |
+---+---+   +
ELISP> (qa 3 10)
+   +---+---+---+---+---+---+---+---+---+
| * |   |       |           |   | *   * |
+   +   +   +---+---+   +---+   +   +   +
| *   *     |       |           | * | * |
+   +   +---+   +---+   +---+---+   +   +
|   | *   *   *   *   *   *   *   * | * |
+---+---+---+---+---+---+---+---+---+   +

今迄のように、LOGファイルに記録しなくても、さっと、問い合わせと結果が 表示出来るようになった。

macro

上記を書いている時、面倒だなあ。間違いそうと想った。 そう、文字列を集積してく部分ね。案の定、括弧の書き間違いで、酷いエラー を誘発した。ならば、マクロか。まて、それは最後の手段。

今回に限れば、これで行けるはず。この定義を利用すれば、princをscに書き 換えするだけで済む。間違えようが無い。

(defun sc (v)
  (setq s (concat s v)))

これに汎用性を持たせるとしたら、そうはいかなくなる。蓄積変数 s が決め 打ちになってるからね。どうしてもマクロの出番。

(defmacro sc (n v)
  `(setq ,n (concat ,n ,v)))

バッククォートの内側で、冒頭にカンマを付けると、その部分が展開される。

ELISP> (macroexpand '(sc name val))
(setq name
      (concat name val))

こんな風に展開された。指示したS式から、新たな式を作成、それが実行され るんだ。

ELISP> (defvar aa "")
aa
ELISP> (sc aa "hello")
"hello"
ELISP> (sc aa "-world")
"hello-world"
ELISP> aa
"hello-world"
ELISP> (defvar z "")
z
ELISP> (sc z "I am ")
"I am "
ELISP> (sc z "macro")
"I am macro"
ELISP> z
"I am macro"

commonLisp/elispのマクロは注意して作成しないと、名前衝突とかのややこし い問題が生じる。だから、On LispだとかLET OVER LAMBDAなんて言う指南本が 出版されるのな。

それに対してschemeは、違ったプローチでマクロを実現してる。そのあたりの 喧喧諤諤なやりとりが、 マクロとか に有る。懐しいな。おっ、 黒田節

networkx and …

たまには、本屋で情報収集と言う立ち読みを実施。流行はやはり、生成AIだ。 日経リナとかソフトウェアは言うに及ばず、インターフェース誌も大特集を組 んでいた。CQ誌でさえも、アマチュア無線の未来を占ってもらっていたぞ。

そんな中で、networkXを使って、ネットワーク図を書きましょうと言うのが目 にとまった。頭の中から溢れ落ちないように、そーと家に持ち帰り。

pipで入れるのは、お約束なんだけど、色々と連鎖してないか?

PiPy networkX して、そこから、source codeをクリック。githubに行きつく。 これって、単一故障点。怖い世の中だのう。

networkx/pyproject.toml これがインストール仕様書なのかなあ。なんか、関 連品が沢山列挙されてるんだけど。。。

とりあえず入れた。単独ではいった。カジュアルには、描画にmatplotlibを使 うみたいだけど、先客で用意されてたので、追加は無かったのかな。

解説ページを検索。割とポピュラーなのね。知らない事ばかり。

Software for Complex Networks Tutorial

[Python]NetworkXの使い方まとめ

Pythonでネットワーク分析【図解でわかりやすく解説】

応用として、どんなものが考えられるのだろう? 例では最短経路の探索。 カーナビに搭載されてる? 駅スパートにも入ってるかな。 プロバイダーな人なら、ネットワークのパケットを何処に流したら得かのシュ ミレーションに利用できそう。まて、そんなのコミコミでBGPだかに組み込ま れているか。

更に近傍を探ってみると、 Welcome to nx-guides! こんなのが出てきた。数独の解法に役だちます。フェースブックで、繋りの分 析もできます。世界は6ホップだかで、繋っているというあれだな。オイラー の所から、バイデン閣下まで、6ホップで届くのか。多分、あの人に連絡すれ ば、あの人が米国の誰かを紹介してくれるだろう。2ホップで米国まで到達。 たまには、あの人にpingしてみるかな。

あと、大事な応用を忘れていた。ググるのページランクね。一杯リンクが貼ら れているページは重要なサイトだろうっていうやつ。ノードに繋るエッジ数そ のものじゃん。ググルの開発者は、これに目をつけたんだな。

例によってgithubから頂いてくると、サンプルが一杯あって、楽しめるぞ。 ただ pip するだけじゃ、もったいない。

matplotlibってグラフを扱かうライブラリィーとばかり思っていたけど、図形 まで描画できるのね。Matplotlib

あっ、こんな記事もあるな。 Pythonのスーパーセットで高速な新プログラミング言語「Mojo」

python++ って、言うらしい。猫も杓子も、、、だな。


This year's Index

Home