Emacs sắp xếp ma trận


8

Vì tôi thấy mình viết rất nhiều ma trận và bảng, tôi đang tìm cách sắp xếp các số độc đáo trong Emacs (tương tự như gói căn chỉnh trong vim). Tôi thấy rằng có sự điều chỉnh phù hợp, nhưng tôi không thể làm cho nó hoạt động theo cách tôi muốn. Có cách nào để căn chỉnh các số theo số thập phân của chúng không --- và nếu không có số thập phân nào thẳng hàng trước các số thập phân khác. Cũng thật tốt khi có thể căn chỉnh tại 'hàng nghìn' dải phân cách và căn chỉnh các số phức. Tốt nhất là có hai khoảng trắng ở giữa các số để dễ đọc. Đây là một ví dụ:

Đầu vào:

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

Sản phẩm chất lượng:

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

Ngoài ra, để làm cho nó dễ dàng hơn một chút (không có dấu phân cách hàng nghìn và số phức):

Đầu vào:

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

Sản phẩm chất lượng:

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

Cảm ơn rất nhiều.

Câu trả lời:


5

Đ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:

  1. 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.
  2. Tôi cũng không thử phân tích số phức.
  3. 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.
  4. Cuối cùng, tôi đã phải từ bỏ align-regexpvì 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 spacerbiế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))))

Công việc tuyệt vời Tôi nghĩ rằng bạn vẫn nên chia sẻ mã ở đây. Câu trả lời của bạn sẽ vô ích nếu liên kết pastebin bị chết. Tôi đã thấy các đoạn mã dài hơn nhiều so với 122 dòng trên SE :)
Kaushal Modi

Wooow, cảm ơn bạn rất nhiều. Tôi xin lỗi vì đã gây ra cho bạn nhiều công việc như vậy, tôi đã hy vọng một số regex hoặc plugin ưa thích có thể thực hiện công việc. Đó chính xác là những gì tôi đang tìm kiếm mặc dù. Tuy nhiên tôi không thể làm cho nó hoạt động. Làm cách nào để sử dụng nó (xin lỗi tôi không có nhiều kinh nghiệm về lisp)? Tôi đã cố gắng đánh dấu khu vực và gọi ma trận / căn chỉnh của tôi, nhưng nó gây ra lỗi sau: "Attemnt để đặt biểu tượng không đổi: t"
DayAndNight

@DayAndNight điều này thực sự kỳ lạ. Tôi không thể tìm thấy một nơi mà lỗi này có thể xảy ra. Nhưng nếu bạn có thể cho tôi một dữ liệu ví dụ, thì cơ hội của tôi sẽ tốt hơn. Bạn có thể không cần đánh dấu khu vực trước khi gọi my/align-matrix. Nếu các số nằm trong một cái gì đó Emacs coi là một loại dấu ngoặc đơn (thường là bất kỳ ai của [], (), {}), thì mã sẽ tự nỗ lực để tìm khu vực đó.
wvxvw
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.