Esto me llevó bastante más tiempo del que originalmente estimé, y el código es demasiado largo para publicarlo aquí, así que lo publiqué en Patebin: http://pastebin.com/Cw82x11i
Sin embargo, no está completamente completo y puede usar algo más de trabajo, por lo que si alguien tiene sugerencias o contribuciones, podría reorganizar esto como un repositorio de Git en algún lugar / volver a publicar esto en la wiki de Emacs.
Pocos puntos importantes:
- No se ha intentado atender matrices con delimitadores que no sean espacios.
- Tampoco intenté analizar números complejos.
- El tratamiento de las entradas no numéricas es diferente al de su ejemplo (para ser sincero, realmente no sabría cómo analizarlo exactamente de la manera que desea. Mi conjetura es que el punto y coma es el delimitador de fila Matlab / Octave , pero si trato de hacerlo más genérico, es realmente difícil entenderlo. Además, supongo que la elipsis es la forma de Matlab / Octave de decirle al intérprete que la declaración continúa en la siguiente línea, pero, nuevamente, tratar de hacer esto más genérico sería realmente difícil. En cambio, solo estoy tratando cualquier valor no numérico que encuentro como si fuera un número entero.
- Finalmente, tuve que renunciar
align-regexp
porque era demasiado complicado intentar alinearlo exactamente usando la regla que parece tener en mente.
Así es como se vería:
;; 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 ];
PD. Puede ajustar el espacio entre las columnas cambiando el valor de la spacer
variable.
OK, también he hecho un pequeño refinamiento al código donde ahora puede pedir que la cadena se complete entre las columnas.
(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 los números están dentro de algo que Emacs trata como una especie de paréntesis (generalmente cualquiera de [], (), {}), entonces el código hará un esfuerzo por encontrar esa región por sí mismo.