Điều này khiến tôi mất nhiều thời gian hơn so với ước tính ban đầu và mã này hơi dài để đăng tất cả ở đây, vì vậy tôi đã đăng nó lên Patebin: http://pastebin.com/Cw82x11i
Mặc dù nó không hoàn toàn đầy đủ và nó có thể sử dụng thêm một số công việc, vì vậy nếu có ai có đề xuất hoặc đóng góp, tôi có thể sắp xếp lại đây là kho lưu trữ Git ở đâu đó / đăng lại trang này cho wiki Emacs.
Vài điểm quan trọng:
- Không có nỗ lực đã được thực hiện để phục vụ cho ma trận với các dấu phân cách khác với không gian.
- Tôi cũng không thử phân tích số phức.
- Cách xử lý các mục không phải là số khác với ví dụ trong ví dụ của bạn (thành thật mà nói, tôi thực sự sẽ không biết cách phân tích nó theo cách bạn muốn. Tôi đoán là dấu chấm phẩy là dấu phân cách hàng Matlab / Octave , nhưng nếu tôi cố gắng làm cho nó chung chung hơn, thật khó để quấn đầu xung quanh nó. Ngoài ra, tôi đoán rằng dấu chấm lửng là cách Matlab / Octave nói với người phiên dịch rằng câu lệnh tiếp tục ở dòng tiếp theo, nhưng, một lần nữa, cố gắng làm cho nó chung chung hơn sẽ thực sự khó khăn. Thay vào đó, tôi chỉ đối xử với bất kỳ giá trị không phải là số nào mà tôi gặp phải như thể nó là một số nguyên.
- Cuối cùng, tôi đã phải từ bỏ
align-regexp
vì nó quá phức tạp để cố gắng làm cho nó căn chỉnh chính xác bằng cách sử dụng quy tắc mà bạn dường như có trong tâm trí.
Đây là những gì nó sẽ trông như thế nào:
;; 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 ];
Tái bút Bạn có thể điều chỉnh không gian giữa các cột bằng cách thay đổi giá trị của spacer
biến.
OK, tôi cũng đã thực hiện một chút tinh chỉnh cho mã nơi mà bây giờ nó có thể yêu cầu chuỗi điền vào giữa các cột.
(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))))