Isso me levou um pouco mais de tempo do que o estimado originalmente, e o código é um pouco longo para postar tudo aqui, então eu o publiquei em Patebin: http://pastebin.com/Cw82x11i
No entanto, ele não está totalmente completo e pode usar mais trabalho; portanto, se alguém tiver sugestões ou contribuições, eu posso reorganizá-lo como um repositório Git em algum lugar / repassá-lo para o wiki do Emacs.
Alguns pontos importantes:
- Nenhuma tentativa foi feita para atender matrizes com delimitadores que não sejam espaços.
- Também não tentei analisar números complexos.
- O tratamento de entradas não numéricas é diferente do seu exemplo (para ser honesto, eu realmente não saberia como analisá-lo exatamente da maneira que você deseja. Meu palpite é que o ponto-e-vírgula é o delimitador de linhas Matlab / Octave , mas se eu tentar torná-lo mais genérico, é realmente difícil entender o assunto.Também acho que as reticências são a maneira do Matlab / Octave de dizer ao intérprete que a declaração continua na próxima linha, mas, mais uma vez, tentar tornar isso mais genérico seria muito difícil.Em vez disso, estou apenas tratando qualquer valor não numérico que encontro como se fosse um número inteiro.
- Finalmente, tive que desistir
align-regexp
porque era muito complicado tentar alinhar exatamente usando a regra que você parece ter em mente.
Aqui está o que seria:
;; 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. Você pode ajustar o espaço entre as colunas alterando o valor da spacer
variável.
OK, também fiz um pequeno refinamento no código, onde ele agora pode solicitar que a string seja preenchida entre as colunas.
(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
. Se os números estiverem dentro de algo que o Emacs trata como uma espécie de parênteses (geralmente qualquer um de [], (), {})), o código fará um esforço para encontrar essa região por conta própria.