calc-eval-format

何らかの計算結果をソース内で定数とする場合、コメントに計算式を残しておきたいことがあります。calc-evalを使うと再利用できて便利ではあるのですが、ちょっと見栄えが悪いです。

(kill-new (calc-eval
           (format "sqrt((%f - %f)^2 + (%f - %f)^2)"
                   1 2 3 4)))

さらに変数が多くなった場合、式との対応が分かりづらくなります。そこで以下のように書けるようにしました。ちなみにcalc-eval自身にもformat-stringと同じ機能はあったりします。詳しくはinfo:Calc#Calling Calc from Your Programs

(calc-eval-format "sqrt((a - b)^2 + (c - a)^2)"
                  a = 1
                  b = 2
                  c = 3)

式をクオートされた文字列ではなく、以下のようにそのまま書きたかったのですがいたらず。

(calc-format eval sqrt((a - b)^2 + (c - a)^2)
             with a = 1
                  b = 2
                  c = 3)

loopマクロの実装を見ればなんとかなるかもと思ったのですが、たいへんそうだったので今後の課題です。

(defmacro calc-eval-format (expr-str &rest vars)
  (let ((expr (gensym))
        (result (gensym)))
    `(let ((case-fold-search nil))
       (setq ,expr ,expr-str)
       (loop for sym in '(,@vars) by #'cdddr
             for eq-sym in (cdr '(,@vars)) by #'cdddr
             for val in (cddr '(,@vars)) by #'cdddr
             do
             (unless (and (symbolp sym)
                          (numberp val)
                          (eq '= eq-sym))
               (error "Wrong format of variables"))
             (setq ,expr (replace-regexp-in-string
                          (concat "\\<" (symbol-name sym) "\\>")
                          (number-to-string val)
                          ,expr t)))
       (setq ,result (calc-eval ,expr))
       (if (stringp ,result)
           (kill-new ,result)
         (if (listp ,result)
             (kill-new (cadr ,result))
           ,result)))))

gist