TeX 数式ラベル入力補助用 elisp -- texlabel.el v1.0.0

texlabel.el とは

TeXの数式ラベル入力補助用の elisp です。
TeXの数式ラベルをいちいち打つのは面倒ですよね。
ラベル名を考えるのすら面倒です。
そんな悩みを解決してくれるようなelispを作りました。

概要

コマンド1つで適切な位置に番号順の数式ラベルを挿入してくれます。
これにより、文章整形後の式番号とソースコードのラベルの番号が対応します。

実行例

基本

以下のようなTeXファイルで texlabel.el のコマンド(M-x texlabel-auto-labeling-default)を実行すると、

次のように、適切な位置にラベルが挿入されます。

標準では「eq:」をプレフィックスとしたラベルが挿入されます。

ラベルの貼り替え

コピペや編集でラベルの順番がバラバラになったとしても、

リファレンスまで含めて貼り替えてくれます。

上のキャプチャ画面では「eq:5」のラベルとリファレンスが共に「eq:1」に変更されています。

特殊ラベルの処理

「eq:数字」以外の特別な名前のラベルがあるときは、

特殊ラベルのみそのままにします。(M-x texlabel-auto-labeling-force を使った場合は特殊ラベルも貼り替えます。)

ただし、整形後の文書の数式番号とラベル名が一致するように、特殊ラベルがある場合もラベル番号はインクリメントします。

インストール

EmacsWiki から texlabel.el をダウンロードして、load-path の通っているフォルダに配置してください。

auto-install.el をインストール済みであれば

 M-x install-elisp-from-emacswiki texlabel.el

を実行するとファイルがダウンロードされるので、C-c C-c でインストールできます。

設定

以下の記述を .emacs ファイル (.emacs.el または .emacs.d/init.el) に追加してください。

(require 'texlabel nil t)

YaTeX を使っている人は以下のように, 適当なコマンド(C-c C-l)に M-x texlabel-auto-labeling-default を割り当てておくとよいでしょう。

(add-hook 'yatex-mode-load-hook
	  '(lambda ()
	    (define-key YaTeX-mode-map (kbd "C-c C-l") 'texlabel-auto-labeling-default)))

YaTeX を使わずに LaTeX-mode を使っている人は次のようにすればキーマップを設定できます。

(add-hook 'latex-mode-hook
	  '(lambda () (define-key latex-mode-map (kbd "C-c C-l") 'texlabel-auto-labeling-default)))

基本的な使い方

M-x texlabel-auto-labeling-default (もしくは上記で設定したコマンド)を実行すると、数式ラベルが自動的に挿入されます。

数式ラベルが挿入可能な箇所すべてに番号順のラベルを挿入するので、整形後の dvi ファイル上での式番号とラベルの番号が一致します。

自前の数式環境や改行コマンドを定義している場合はラベル挿入位置をうまく判別できない可能性がありますので、下記の「細かい設定」を参考にしてください。

細かい設定

細かい設定はカスタマイズ変数で行えます。

M-x customize-group RET texlabel RET

としてカスタマイズ変数を変更するか、.emacs 内に設定を記述してください。

カスタマイズ変数

以下のようなカスタマイズ変数が定義されています。

  • texlabel-math-env-list
    • 数式環境名のリスト。標準の数式環境と amsmath の数式環境はサポートしています。その他の数式環境を定義している場合はこのリストに追加してください。
  • texlabel-non-sep-math-env-list
    • multline のような改行してもラベルが単一の数式環境を表すリスト。標準では multline のみ。
  • texlabel-refcmd-list
    • リファレンス用のコマンドリスト。標準では ref と eqref のみ。
  • texlabel-notag-list
    • ラベルなしを指示するコマンドのリスト。標準では \notag と \nonumber のみ。
  • texlabel-line-sep-list
    • 改行コマンドのリスト。標準では \\ のみ。

  • texlabel-pre-label-string
    • ラベルコマンドの直前に挿入される文字列。標準では空白2つ分。

設定例

設定例1

TeXのスタイルファイル内で、以下のような改行+notagを表すコマンドを定義している場合、

\newcommand{\nn}{\nonumber \\}

.emacs 内で以下のように、texlabel-notag-list と texlabel-line-sep-list に \nn を追加してください。

(add-to-list 'texlabel-line-sep-list "\\nn")
(add-to-list 'texlabel-notag-list "\\nn")
設定例2

ラベルを独立した行に配置したい場合は、texlabel-pre-label-string を改行文字にしてください。

(setq  texlabel-pre-label-string "\n")

以下のようにラベルが挿入されます。

Tips

ファイルの分割に関して

現在、texlabel.el は input や include を用いたファイルの読み込みには対応していません。理由は、

  1. input 先のファイルのラベルの挿入や貼り替えを行うと、変更に気づかない可能性が高く、問題が多い。
  2. ファイルを分割してなお別ファイルから引用するような式はそもそも固有のラベル名を付けるべきである。
  3. input の主な用途は章ごとのファイル分割なので、式番号はリセットされることが多い。

の3つです。1番目の問題をうまく解決する手段を思いつくか、熱い要望がない限り今後も対応の予定はありません。

input で章ごとにファイルを分割している場合は、対応策として章ごとに texlabel の prefix を変更するとよいでしょう。
texlabel-prefix がバッファローカルな変数になっているので、ファイルの末尾に

%%% Local Variables: %%%
%%% texlabel-prefix: "quant" %%%
%%% End: %%%

のような文章を挿入することで, ファイルごとに prefix を変更することが可能です。

例えば, 以下のキャプチャ画面では式の prefix を "quant" に変更しています。

その他

関数

以下の関数が使用可能ですが、texlabel-auto-labeling-default 以外はほとんど使わないでしょう。

  • texlabel-auto-labeling-default
    • 自動的に数式ラベルを挿入します。「eq;数字」以外のラベルはそのまま残します。
  • texlabel-auto-labeling
    • プレフィックス(デフォルトは「eq:」)を対話的に入力して数式ラベルを挿入します。編集中のファイルのデフォルトのプレフィックスも一時的に変更されます。
  • texlabel-auto-labeling-force
    • 自動的に数式ラベルを挿入します。「eq:数字」以外のラベルも「eq:数字」形式に強制的に貼り替えます。
  • texlabel-change-default-prefix

Problem 22

(require 'calc-ext)
(defvar calc-command-flags nil)

(with-temp-buffer
  (insert-file-contents-literally (car argv))
  (let (name-lst (i 1) (sum 0))
    (while (re-search-forward "\"\\([A-Z]+?\\)\"" nil t)
      (setq name-lst (cons (match-string-no-properties 1) name-lst)))
    (setq name-lst (sort name-lst #'string<))

    (while name-lst
      (let ((name (car name-lst)) score)
      (setq score (apply #'+ (mapcar #'(lambda (char) (1+ (- char ?A))) (append name nil))))
      (setq sum (math-add sum (math-mul score i)))
      (setq i (1+ i))
      (setq name-lst (cdr name-lst))))
    (princ (math-format-value sum))
    (princ "\n") ))

Problem 21

(require 'cl)
(require 'calc-ext)
(defvar calc-command-flags nil)

(defun hash-to-alist (hash)
  (let (alist)
    (maphash #'(lambda (key val) (push (cons key val) alist)) hash)
    alist ))

(defun find-factor (num)
  (catch 'found
    (let ((i 2))
      (while (math-lessp (math-sub i 1) (math-floor (math-sqrt num)))
	(if (math-equal (math-mod num i) 0)
	    (throw 'found i)
	  (setq i (math-add i 1))) ))))

(defun factorize (num)
  (if (= num 1) nil
    (let ((p-table (make-hash-table)) p val)
      (while (setq p (find-factor num))
	(setq num (/ num p))
	(if (setq val (gethash p p-table))
	    (puthash p (1+ val) p-table)
	  (puthash p 1 p-table) ))
      (if (setq val (gethash num p-table))
	  (puthash num (1+ val) p-table)
	(puthash num 1 p-table) )
      (hash-to-alist p-table) )))

(defun divisor-sum (num)
  (let ((prime-alist (factorize num)) (rst 1))
    (while prime-alist
      (let* ((pair (car prime-alist)) (p (car pair)) (n (cdr pair)) (sum 0))
	(dotimes (i (1+ n))
	  (setq sum (+ (math-pow p i) sum)) )
	(setq prime-alist (cdr prime-alist))
	(setq rst (* rst sum)) ))
    (- rst num) ))

(defun amicable (num)
  (let ((candidate (divisor-sum num)))
    (if (and (not (eq candidate num)) (eq num (divisor-sum candidate)))
	candidate) ))

(let ((i 1) (tmp 0) (sum 0))
  (while (<= i (string-to-number (car argv)))
    (if (setq tmp (amicable i))
	(setq sum (+ sum tmp)) )
    (setq i (1+ i)))
  (princ (math-format-value sum))
  (princ "\n") )

Problem 20

(require 'calc-ext)
(defvar calc-command-flags nil)

(defun factorial (n)
  (let ((rst 1))
    (while (math-lessp 0 n)
      (setq rst (math-mul rst n))
      (setq n (math-sub n 1)) )
    rst ))

(defun digit-sum (n)
  (let* ((str (math-format-value n))
	 (len (length str))
	 (sum 0))
    (dotimes (i len sum)
      (setq sum (+ sum (- (aref str i) ?0))) )))

(princ (digit-sum (factorial (string-to-number (car argv)))))
(princ "\n")

Problem 19

(defvar month-day-alist '((1 . 31)
			  (2 . 28)
			  (3 . 31)
			  (4 . 30)
			  (5 . 31)
			  (6 . 30)
			  (7 . 31)
			  (8 . 31)
			  (9 . 30)
			  (10 . 31)
			  (11 . 30)
			  (12 . 31)))

(defun assq-val (key alist)
  (cdr (assq key alist)))


(defun next-month (year month day)
  (let ((days (assq-val month month-day-alist)))
    (if (eq month 2)
	(if (or (eq (% year 400) 0)
		(and (not (eq (% year 100) 0))
		     (eq (% year 4) 0))) ; leap year
	    (setq days (1+ days)) ))
    (if (eq month 12)
	(progn
	  (setq year (1+ year))
	  (setq month 1) )
      (setq month (1+ month)) )
    (setq day (% (+ day days) 7))
    (list year month day) ))

(let ((year-month-day '(1900 1 1)) (sum 0))
  (while (not (eq (car year-month-day) 1901))
    (setq year-month-day (apply #'next-month year-month-day)) )
  (while (not (eq (car year-month-day) 2001))
    (if (eq (nth 2 year-month-day) 0)
	(setq sum (1+ sum)) )
    (setq year-month-day (apply #'next-month year-month-day)) )
  (princ sum)
  (princ "\n") )

Probrem 18

(defun read-txt-numbers (file-name)
  (let (num-lst)
    (with-temp-buffer
      (insert-file-contents-literally file-name)
      (goto-char (point-min))
      (while (re-search-forward "[0-9]+" nil t)
	(setq num-lst (cons (string-to-number (match-string 0)) num-lst)) ))
    (nreverse num-lst) ))

(defun make-triangle (lst)
  (let (rst (n 1))
    (while lst
      (let ((i 0) (tmp (make-vector n 0)))
	(while (< i n)
	  (aset tmp i (car lst))
	  (setq i (1+ i))
	  (setq lst (cdr lst)) )
	(setq rst (cons tmp rst)) )
      (setq n (1+ n)))
    (nreverse rst) ))

(defun next-step (previous-sum-vec current-vec)
  (let* ((len (length current-vec))
	 (current-sum-vec (make-vector len 0))
	 (larger-sum 0))
    (dotimes (i len current-sum-vec)
      (setq larger-sum
	    (cond
	      ((eq i 0) (aref previous-sum-vec i))
	      ((eq i (1- len)) (aref previous-sum-vec (1- i)))
	      (t (max (aref previous-sum-vec i) (aref previous-sum-vec (1- i)))) ))
      (aset current-sum-vec i (+ (aref current-vec i) larger-sum)) )))

(let* ((triangle (make-triangle (read-txt-numbers (car argv))))
       (previous-sum-vec (car triangle))
       (current-vec))
  (princ triangle)
  (setq triangle (cdr triangle))
  (while triangle
    (setq current-vec (car triangle))
    (setq previous-sum-vec (next-step previous-sum-vec current-vec))
    (setq triangle (cdr triangle)) )
  (princ (apply #'max (append previous-sum-vec nil)))
  (princ "\n") )
メモ
  • triangle.txt を用意して, 次のコマンドを実行.
emacs -script 18.el triangle.txt
  • ベクトルを list に変換するときは, append で nil をくっつければOK.
 (append [1 2 3] nil) ;=> (1 2 3)