Emacs richten Matrizen aus


8

Da ich viele Matrizen und Tabellen schreibe, suche ich nach einer Möglichkeit, Zahlen in Emacs gut auszurichten (ähnlich dem Ausrichtungspaket in vim). Ich habe festgestellt, dass es align-regexp gibt, aber ich konnte es nicht so zum Laufen bringen, wie ich es wollte. Gibt es eine Möglichkeit, Zahlen an ihren Dezimalstellen auszurichten - und wenn keine Dezimalstellen vor den anderen Dezimalstellen ausgerichtet sind? Es wäre auch schön, wenn man sich an Tausenden-Trennzeichen ausrichten und komplexe Zahlen ausrichten könnte. Zur besseren Lesbarkeit vorzugsweise mit zwei Leerzeichen zwischen Zahlen. Hier ist ein Beispiel:

Eingang:

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

Gewünschte Ausgabe:

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

Alternativ, um es ein bisschen einfacher zu machen (ohne Tausendertrennzeichen und komplexe Zahlen):

Eingang:

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

Gewünschte Ausgabe:

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

Vielen Dank.

Antworten:


5

Das hat mich viel länger gekostet, als ich ursprünglich angenommen hatte, und der Code ist etwas zu lang, um alles hier zu posten. Deshalb habe ich ihn auf Patebin gepostet: http://pastebin.com/Cw82x11i

Es ist jedoch nicht vollständig und kann etwas mehr Arbeit erfordern. Wenn also jemand Vorschläge oder Beiträge hat, kann ich dies irgendwo als Git-Repository neu arrangieren / dies im Emacs-Wiki erneut veröffentlichen.

Einige wichtige Punkte:

  1. Es wurde kein Versuch unternommen, Matrizen mit anderen Trennzeichen als Leerzeichen zu berücksichtigen.
  2. Ich habe auch nicht versucht, komplexe Zahlen zu analysieren.
  3. Die Behandlung nicht numerischer Einträge unterscheidet sich von der in Ihrem Beispiel (um ehrlich zu sein, würde ich nicht wirklich wissen, wie man sie genau so analysiert, wie Sie es möchten. Ich vermute, dass das Semikolon das Matlab / Octave-Zeilenbegrenzer ist , aber wenn ich versuche, es allgemeiner zu gestalten, ist es wirklich schwierig, meinen Kopf darum zu wickeln. Ich vermute auch, dass die Ellipse die Matlab / Octave-Methode ist, um dem Interpreten mitzuteilen, dass die Aussage in der nächsten Zeile fortgesetzt wird, aber, Auch hier wäre es sehr schwierig, dies allgemeiner zu gestalten. Stattdessen behandle ich nur den nicht numerischen Wert, auf den ich stoße, als wäre es eine ganze Zahl.
  4. Schließlich musste ich das aufgeben, align-regexpweil es zu kompliziert war, es genau nach der Regel auszurichten, die Sie anscheinend im Sinn hatten.

So würde es aussehen:

;; 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. Sie können den Abstand zwischen den Spalten anpassen, indem Sie den Wert der spacerVariablen ändern .

OK, ich habe auch den Code ein wenig verfeinert, wo er nun nach dem Ausfüllen der Zeichenfolge zwischen den Spalten fragen kann.

(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))))

Tolle Arbeit. Ich denke, dass Sie den Code hier noch teilen sollten. Ihre Antwort ist nutzlos, wenn der Pastebin-Link nicht mehr funktioniert. Ich habe viel viel längere Code-Schnipsel als 122 Zeilen auf SE gesehen :)
Kaushal Modi

Wooow, vielen Dank. Es tut mir leid, Ihnen so viel Arbeit zu bereiten. Ich hatte gehofft, dass ein ausgefallener Regex oder ein Plugin den Job machen könnten. Es ist genau das, wonach ich gesucht habe. Ich kann es jedoch nicht zum Laufen bringen. Wie benutze ich es (sorry, ich habe nicht viel Erfahrung mit Lisp)? Ich habe versucht, die Region zu markieren und meine / align-Matrix aufzurufen, aber es gibt mir den folgenden Fehler: "Versuch, ein konstantes Symbol zu setzen: t"
DayAndNight

@ DayAndNight das ist wirklich seltsam. Ich kann keinen Ort finden, an dem dieser Fehler auftreten kann. Wenn Sie mir jedoch Beispieldaten geben können, sind meine Chancen besser. Möglicherweise müssen Sie die Region nicht markieren, bevor Sie anrufen my/align-matrix. Wenn sich die Zahlen in etwas befinden, das Emacs als eine Art Klammer behandelt (normalerweise eine von [], (), {}), bemüht sich der Code, diese Region selbst zu finden.
wvxvw
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.