>> 古い記事: インストーラ不使用でBazaar/Bazaar Explorerを使う(Windows)

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

(書きかけ)

Emacs Lisp メモ: タイマー

書きかけです。


timer.el にいろいろ書いてある。

----

triggered / not triggered
idle-delay の有無

アクティブ / 非アクティブ

----
■ タイマーオブジェクト
タイマーオブジェクトは単なるベクタ。
(type-of timer) したり timerp の定義を見ると分かる。

[triggered-p high-seconds low-seconds usecs repeat-delay function args idle-delay]

high-seconds, low-seconds, usecs については
↓ここらへんを参考に
GNU Emacs Lispリファレンスマニュアル: Time of Day

high * 2**16 + low が UNIX時間になる。

----
■ triggered

(setq timer (run-with-timer 5 nil 'foo-func))
;; ↓foo-func が実行される前に評価
(aref timer 0) ;=> nil
;; ↓foo-func が実行された後で評価
(aref timer 0) ;=> t
;; run-with-idle-timer でも同じ

----

(memq timer timer-idle-list)
動いている時:   non-nil
止まっている時: nil

(setq foo-timer (run-with-timer 5 5 (lambda () nil)))
(type-of foo-timer) ;=> vector

[triggered-p high-seconds low-seconds usecs repeat-delay function args idle-delay]
triggered-p はタイマーがアクティブな場合 nil (トリガーされるのを待っている)。
アクティブでない場合 t (理屈で言えば「すでにトリガーされた」状態)。

----
;; timer-list と timer-idle-list の違い

(setq timer (run-with-timer 60 nil (lambda () nil)))
(memq timer timer-list) ;=> non-nil
(memq timer timer-idle-list) ;=> nil
(cancel-timer timer)
(memq timer timer-list) ;=> nil
(memq timer timer-idle-list) ;=> nil

(setq timer (run-with-idle-timer 60 nil (lambda () nil)))
(memq timer timer-list) ;=> nil
(memq timer timer-idle-list) ;=> non-nil
(cancel-timer timer)
(memq timer timer-list) ;=> nil
(memq timer timer-idle-list) ;=> nil
idle でない場合 timer-list に、
idle の場合 timer-idle-list に入る。

----

;; run-with-timer と run-with-idle-timer で作られるタイマーの違い


(setq timer (run-with-timer 60 nil (lambda () nil)))
(aref timer 7) ;=> nil

(setq timer (run-with-idle-timer 60 nil (lambda () nil)))
(aref timer 7) ;=> idle
(type-of (aref timer 7)) ;=> symbol
(eq 'idle (aref timer 7)) ;=> t
----

run-with-timer
run-with-idle-timer
cancel-timer

cancel-timer-internal
timer.el 内部で使うためのもの、とあるので、普通は使う必要なさげ

timer-activate
timer-idle-list
timer-list
timer-activate-when-idle
timer-set-idle-time

----

何かを定期的に実行させるようにしたい。
定期的な実行を開始させるコマンドと停止させるコマンドを作りたい。

(defvar foo-timer)

(defun foo-start ()
  (interactive)
  (when (timer-running-p foo-timer)
    (foo-stop))
  (setq foo-timer (run-with-timer ...))
  )

(defun foo-stop ()
  (interactive)
  (cancel-timer foo-timer)
  )
timer-running-p はどのような実装が適切か?

----

(cancel-timer timer) した後で
(setq timer nil) する方法もあるが、
この場合「cancel-timer した後でタイマー変数に nil をセットしなければならない」
というお約束を作ることになる。
(ただ、この方法は分かりやすいと言えば分かりやすい)

(cancel-timer timer) することで何かしら状態が変わるのなら、
その情報を利用した方がいいのでは?

timer-(idle)-list を見る場合
idle かそうでないかでどちらのリストに入るか異なるので、
idle かそうでないかを意識して使わなければならない。

↓こうすればOK?
(defun timer-running-p (timer)
  (or (memq timer timer-list)
      (memq timer timer-idle-list)))

----

タイマー実行する局面を限定したい場合の例

これも eldoc が参考になる。
例)
インターバル実行を開始したバッファ以外では実行させない
バッファが特定のモードでない場合は実行させない
リージョンを設定している場合は実行させない
ミニバッファで入力している場合は実行させない

たとえば、replace-string を定期的に実行して
バッファ内の文字列を置換する、という場合
何かのコマンドを実行するためにミニバッファへの入力モードになり、
そこで入力した foo という文字列が置換されては困る
、といった場合。

小さな関数であれば
関数のbody全体を if や when で囲む。

大きな関数の場合は間に代理の
プロキシ? エージェント? マネージャ?を挟むようにしてみる。

……というあたりを踏まえて、次のようなテンプレを作ってみたり。 マクロ化できる? そこまでしなくてもいい?


(defun timer-running-p (timer)
  (or (memq timer timer-list)
      (memq timer timer-idle-list)))

;;;;

(defvar foo-lib:foo-func-timer nil)

;;;;

(defun foo-lib:foo-func ()
  ;; メインの処理(長い・複雑)
  (message "%d" (random)))

(defun foo-lib:foo-func-runnable-p ()
  ;; 処理を実行すべきコンテキストにあるかどうかを判断するロジック
  (string= "*scratch*" (buffer-name (current-buffer))))

(defun foo-lib:foo-func-proxy ()
  (when (foo-lib:foo-func-runable-p)
    (foo-lib:foo-func)))

;;;;

;; すでに動いている場合は何もしない
(defun foo-lib:foo-func-start ()
  (interactive)
  (unless (timer-running-p foo-lib:foo-func-timer)
    (setq foo-lib:foo-func-timer (run-with-timer 1 2 'foo-lib:foo-func-proxy))))

;; 一旦停止させて、あらためて開始。要らない?
(defun foo-lib:foo-func-reset ()
  (interactive)
  (when (timer-running-p foo-lib:foo-func-timer)
    (foo-lib:foo-func-stop))
  (setq foo-lib:foo-func-timer
        (run-with-timer 1 2 'foo-lib:foo-func-proxy)))

(defun foo-lib:foo-func-stop ()
  (interactive)
  (cancel-timer foo-lib:foo-func-timer))
■ コールバックの実行間隔の参照・変更
ベクタなので aref, aset を使えばよい。

(aref timer 4)
(aset timer 4 SEC) ; タイマー実行中でも変更できる


■ コールバック内でエラーが起こった場合の処理
何もしないとエラーが起こっても黙殺されるので、
とりあえず condition-case と message で対処してみる。

(defun callback-main ()
  (/ 1 0))

(defun callback ()
  (condition-case nil
      (callback-main) ; エラーが起こるかもしれない処理
    (error ; エラーが起こったら以下を実行
     (progn
       ;; (必要なら)ここら辺りでタイマーを止める
       (message "Something wrong in callback") ;=> *Message* バッファにも出力される
       ))))

(setq timer
      (run-with-timer 0 5 'callback))
バッファとの絡み
タイマーオブジェクトはバッファローカルにする?
メジャーモード終了時にタイマー停止
マイナーモード終了時にタイマー停止

参考(外部リンク)

>> 古い記事: インストーラ不使用でBazaar/Bazaar Explorerを使う(Windows)

** ホームに戻る

コメント

コメントの投稿

管理者にだけ表示を許可する

|
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。