Emacsは行列を整列します


8

私はたくさんの行列やテーブルを書いているので、Emacsで数値を適切に整列する方法を探しています(vimのalignパッケージと同様)。私はalign-regexpがあることを発見しましたが、思い通りに動作させることができませんでした。数値を小数で揃える方法はありますか---小数がない場合、他の小数の前に揃えます。また、「数千」のセパレータで整列し、複素数を整列できると便利です。読みやすくするために、数値の間に2つの空白があることが望ましいです。次に例を示します。

入力:

A = [-15 9 33.34;...
1.0 0.99 1+3i;...
13,000 2 11 ];

望ましい出力:

A = [   -15     9     33.34 ;...
          1.0  -0.99   1+3i ;...
     13,000     2     11    ];

または、少し簡単にするために(「数千」の区切り文字や複素数を使わずに):

入力:

A = [-15 9 33.34;...
1.0 0.99 1;...
13000 2 11 ];

望ましい出力:

A = [  -15     9      33.34 ; ...
         1.0   0.99    1    ; ...
     13000     2      11    ];

どうもありがとう。

回答:


5

これは私が最初に見積もったよりもかなり長い時間を要し、コードはここにすべてを投稿するには少し長すぎるので、Patebinに投稿しました:http ://pastebin.com/Cw82x11i

完全ではありませんが、さらに作業が必要になる可能性があるため、提案や貢献があった場合は、Gitリポジトリのどこかに配置し直したり、Emacs wikiに再投稿したりできます。

いくつかの重要なポイント:

  1. スペース以外の区切り文字を含む行列に対応する試みは行われていません。
  2. 複素数も解析しようとしませんでした。
  3. 非数値のエントリの扱いは、例のそれとは異なります(正直なところ、希望する方法で正確に解析する方法がわかりません。セミコロンはMatlab / Octaveの行区切り文字だと思います、しかし、もっと一般的にしようとすると、頭を包み込むのが本当に難しくなります。また、省略記号は、ステートメントが次の行に続くことをインタプリタに伝えるMatlab / Octaveの方法だと思いますが、繰り返しますが、これをより一般的なものにすることは非常に困難であり、代わりに、発生した数値以外の値を整数であるかのように扱っています。
  4. 最後に、align-regexp念頭に置いているように見えるルールを使用して正確に整列させるのは複雑すぎるため、私はあきらめなければなりませんでした。

これは次のようになります。

;; before
A = [-15 9 33.34;...
1.0 0.99 1;...
13000 2 11 ];

;; after
A = [  -15   9    33.34 ;... 
         1.0 0.99  1    ;... 
     13000   2    11         ];

PS。spacer変数の値を変更することにより、列間のスペースを調整できます。

列の間に文字列を入力するように要求できるように、コードを少し改良しました。

(defun my/string-to-number (line re)
  (let ((matched (string-match re line)))
    (if matched
        (list (match-string 0 line)
              (substring line (length (match-string 0 line))))
      (list nil line))))

(defun my/string-to-double (line)
  (my/string-to-number
   line
   "\\s-*[+-]?[0-9]+\\(?:\\.[0-9]+\\(?:[eE][+-]?[0-9]+\\)?\\)?"))

(defun my/string-to-int (line)
  (my/string-to-number line "\\s-*[+-]?[0-9]+"))

(defun my/vector-transpose (vec)
  (cl-coerce
   (cl-loop for i below (length (aref vec 0))
            collect (cl-coerce 
                     (cl-loop for j below (length vec)
                              collect (aref (aref vec j) i))
                     'vector))
   'vector))

(defun my/align-metric (col num-parser)
  (cl-loop with max-left = 0
           with max-right = 0
           with decimal = 0
           for cell across col
           for nump = (car (funcall num-parser cell))
           for has-decimals = (cl-position ?\. cell) do
           (if nump
               (if has-decimals
                   (progn
                     (setf decimal 1)
                     (when (> has-decimals max-left)
                       (setf max-left has-decimals))
                     (when (> (1- (- (length cell) has-decimals))
                              max-right)
                       (setf max-right (1- (- (length cell) has-decimals)))))
                 (when (> (length cell) max-left)
                   (setf max-left (length cell))))
             (when (> (length cell) max-left)
               (setf max-left (length cell))))
           finally (cl-return (list max-left decimal max-right))))

(defun my/print-matrix (rows metrics num-parser prefix spacer)
  (cl-loop with first-line = t
           for i upfrom 0
           for row across rows do
           (unless first-line (insert prefix))
           (setf first-line nil)
           (cl-loop with first-row = t
                    for cell across row
                    for metric in metrics
                    for has-decimals =
                    (and (cl-position ?\. cell)
                         (car (funcall num-parser cell)))
                    do
                    (unless first-row (insert spacer))
                    (setf first-row nil)
                    (cl-destructuring-bind (left decimal right) metric
                      (if has-decimals
                          (cl-destructuring-bind (whole fraction)
                              (split-string cell "\\.")
                            (insert (make-string (- left (length whole)) ?\ )
                                    whole
                                    "."
                                    fraction
                                    (make-string (- right (length fraction)) ?\ )))
                        (insert (make-string (- left (length cell)) ?\ )
                                cell
                                (make-string (1+ right) ?\ )))))
           (unless (= i (1- (length rows)))
             (insert "\n"))))

(defun my/read-rows (beg end)
  (cl-coerce
   (cl-loop for line in (split-string
                         (buffer-substring-no-properties beg end) "\n")
            collect
            (cl-coerce
             (nreverse
              (cl-loop with result = nil
                       with remaining = line do
                       (cl-destructuring-bind (num remainder)
                           (funcall num-parser remaining)
                         (if num
                             (progn
                               (push (org-trim num) result)
                               (setf remaining remainder))
                           (push (org-trim remaining) result)
                           (cl-return result)))))
             'vector))
   'vector))

(defvar my/parsers '((:double . my/string-to-double)
                     (:int . my/string-to-int)))

(defun my/align-matrix (parser &optional spacer)
  (interactive
   (let ((sym (intern
               (completing-read
                "Parse numbers using: "
                (mapcar 'car my/parsers)
                nil nil nil t ":double")))
         (spacer (if current-prefix-arg
                     (read-string "Interleave with: ")
                   " ")))
     (list sym spacer)))
  (unless spacer (setf spacer " "))
  (let ((num-parser
         (or (cdr (assoc parser my/parsers))
             (and (functionp parser) parser)
             'my/string-to-double))
        beg end)
    (if (region-active-p)
        (setf beg (region-beginning)
              end (region-end))
      (setf end (1- (search-forward-regexp "\\s)" nil t))
            beg (1+ (progn (backward-sexp) (point)))))
    (goto-char beg)
    (let* ((prefix (make-string (current-column) ?\ ))
           (rows (my/read-rows beg end))
           (cols (my/vector-transpose rows))
           (metrics
            (cl-loop for col across cols
                     collect (my/align-metric col num-parser))))
      (delete-region beg end)
      (my/print-matrix rows metrics num-parser prefix spacer))))

お見事。ここでもコードを共有する必要があると思います。ペーストビンのリンクが死んでしまった場合、あなたの答えは役に立たないでしょう。SEで122行よりもはるかに長いコードスニペットを見てきました:)
Kaushal Modi

うわー、どうもありがとうございました。大変お手数をおかけして申し訳ありません。派手な正規表現またはプラグインがうまく機能することを願っています。それはまさに私が探していたものです。しかし、私はそれを機能させることができません。どのように使用しますか(lispでの経験があまりないので申し訳ありません)?リージョンをマークしてmy / align-matrixを呼び出そうとしましたが、次のエラーが発生しました:「定数シンボルを設定する試み:t」
DayAndNight

@DayAndNightこれは本当に奇妙です。このエラーが発生する可能性のある場所が見つかりません。しかし、例のデータを提供していただければ、私の可能性が高まります。を呼び出す前にリージョンをマークする必要がない場合がありmy/align-matrixます。数値がEmacsが一種の括弧(通常は[]、()、{}のいずれか)として扱うものの中にある場合、コードはその領域を独自に検索しようとします。
wvxvw
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.