Cela m'a pris un peu plus de temps que je ne l'avais initialement estimé, et le code est un peu trop long pour tout publier ici, alors je l'ai posté sur Patebin: http://pastebin.com/Cw82x11i
Cependant, il n'est pas entièrement complet et il peut nécessiter plus de travail, donc si quelqu'un a des suggestions ou des contributions, je pourrais réorganiser cela en tant que référentiel Git quelque part / republier cela sur le wiki Emacs.
Quelques points importants:
- Aucune tentative n'a été faite pour répondre aux matrices avec des délimiteurs autres que des espaces.
- Je n'ai pas non plus essayé d'analyser des nombres complexes.
- Le traitement des entrées non numériques est différent de celui de votre exemple (pour être honnête, je ne saurais pas vraiment comment l'analyser exactement comme vous le souhaitez. Je suppose que le point-virgule est le délimiteur de ligne Matlab / Octave , mais si j'essaye de le rendre plus générique, il est vraiment difficile de faire le tour de ma tête. De plus, je suppose que les points de suspension sont la manière Matlab / Octave de dire à l'interprète que la déclaration continue sur la ligne suivante, mais, encore une fois, essayer de rendre cela plus générique serait vraiment difficile. Au lieu de cela, je traite simplement la valeur non numérique que je rencontre comme s'il s'agissait d'un nombre entier.
- Enfin, j'ai dû abandonner le
align-regexp
car il était trop compliqué d'essayer de l'aligner exactement en utilisant la règle que vous semblez avoir en tête.
Voici à quoi cela ressemblerait:
;; 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. Vous pouvez ajuster l'espace entre les colonnes en modifiant la valeur de la spacer
variable.
OK, j'ai également apporté un petit raffinement au code où il peut maintenant demander que la chaîne soit remplie entre les colonnes.
(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))))
my/align-matrix
. Si les nombres se trouvent dans quelque chose qu'Emacs traite comme une sorte de parenthèse (généralement n'importe qui de [], (), {}), alors le code fera un effort pour trouver cette région par lui-même.