Làm cách nào để hoán chuyển hai đối số của hàm trong Python?


11

Làm cách nào tôi có thể trao đổi hai đối số trong một cuộc gọi sang hàm Python?

Nếu tôi đặt pointkhoảng trắng giữa hai đối số này:

self.assertEqual(json.loads(some.data), json_data)

và sau đó M-t( transpose-words), tôi nhận được:

self.assertEqual(json.loads(some.json), data_data)

Mặt khác với CMt ( transpose-sexps) tôi nhận được:

self.assertEqual(json.loadsjson_data, (some.data))

Điều tôi muốn là:

self.assertEqual(json_data, json.loads(some.data))

Có một lệnh sẽ làm điều đó?


2
Tôi chưa thử nhưng Neo Transpose có thể được sử dụng cho việc này; Tuy nhiên, đó là một quá trình gồm 2 bước.
Kaushal Modi

Có một hàm cốt lõi được gọi là transpose-subrlấy một forwardhàm và chuyển nó thành một transposehàm. Vì vậy, nếu chúng ta có c-forward-arglist(chức năng chuyển từ một chức năng này sang chức năng tiếp theo - AFAICT thì điều này không tồn tại) chúng ta sẽ có c-transpose-arglist.
Brendan

Câu trả lời:


4

Đây là điều mà tôi cũng muốn có từ lâu và ở đây tôi đã tìm thấy một số động lực để làm việc với nó. Nó có thể không mạnh lắm, nhưng ở lần thử đầu tiên, nó dường như bao gồm các trường hợp tôi đã thử:

(defun my/calculate-stops ()
  (save-excursion
    (let ((start
           (condition-case e
               (while t (backward-sexp))
             (error (point))))
          stops)
      (push start stops)
      (condition-case e
          (while t
            (forward-sexp)
            (when (looking-at "\\s-*,")
              (push (point) stops)))
        (error (push (point) stops)))
      (nreverse stops))))

(defun my/transpose-args ()
  (interactive)
  (when (looking-at "\\s-") (backward-sexp))
  (cl-loop with p = (point)
           with previous = nil
           for stop on (my/calculate-stops)
           for i upfrom 0
           when (<= p (car stop)) do
           (when previous
             (let* ((end (cadr stop))
                    (whole (buffer-substring previous end))
                    middle last)
               (delete-region previous end)
               (goto-char previous)
               (setf middle (if (> i 1) (- (car stop) previous)
                              (string-match "[^, \\t]" whole 
                                            (- (car stop) previous)))
                     last (if (> i 1) (substring whole 0 middle)
                            (concat (substring whole (- (car stop) previous) middle)
                                    (substring whole 0 (- (car stop) previous)))))
               (insert (substring whole middle) last)))
           (cl-return)
           end do (setf previous (car stop))))

Tôi nhận được điều này khi điểm nằm trên khoảng trắng (hoạt động khi trên dấu phẩy): let *: Đối số kiểu sai: số nguyên hoặc hoặc-đánh dấu-p, nil
Croad Langshan

@CroadLang Sơn việc khắc phục tương đối dễ dàng (xem bên trên). Điều buồn cười tôi đã cố làm theo lời khuyên của Stefan bằng cách viết một bài thay thế forward-sexp-function, và điều đó dường như quá cồng kềnh vì dấu phẩy. Sau đó tôi đã cố gắng sao chép traspose-sexpđể làm điều tương tự, và, một lần nữa, việc phải tính đến dấu phẩy làm cho điều này thực sự khó khăn. Tôi không khẳng định điều này luôn luôn đúng khi liệt kê các dấu phân cách, có thể chỉ bằng một nửa thời gian ...
wvxvw

Điều này không thành công (aa, bb)khi con trỏ ở trên đầu tiên a. Điều này cũng không hoạt động để chuyển vị FOO(aaa *, bbb, ccc). *Bằng cách nào đó làm rối loạn chuyển vị.
ideaman42

Cũng thất bại cho FOO(&aaa, bbb)(& không trao đổi).
ideaman42

4

Tôi sử dụng một biến thể của giao diện transpose-sexpsđó cho trường hợp bạn mô tả và hoán chuyển mọi thứ - được phân tách bằng dấu phẩy hoặc chỉ là thông thường transpose-sexps. Nó cũng để con trỏ vào vị trí thay vì kéo nó về phía trước, điều này hơi khác một chút nhưng cá nhân tôi thích.

(defun my-transpose-sexps ()
  "If point is after certain chars transpose chunks around that.
Otherwise transpose sexps."
  (interactive "*")
  (if (not (looking-back "[,]\\s-*" (point-at-bol)))
      (progn (transpose-sexps 1) (forward-sexp -1))
    (let ((beg (point)) end rhs lhs)
      (while (and (not (eobp))
                  (not (looking-at "\\s-*\\([,]\\|\\s)\\)")))
        (forward-sexp 1))
      (setq rhs (buffer-substring beg (point)))
      (delete-region beg (point))
      (re-search-backward "[,]\\s-*" nil t)
      (setq beg (point))
      (while (and (not (bobp))
                  (not (looking-back "\\([,]\\|\\s(\\)\\s-*" (point-at-bol))))
        (forward-sexp -1))
      (setq lhs (buffer-substring beg (point)))
      (delete-region beg (point))
      (insert rhs)
      (re-search-forward "[,]\\s-*" nil t)
      (save-excursion
        (insert lhs)))))

Đối với tôi, điều này để lại khoảng trắng khi bắt đầu cuộc gọi assertEqual (bắt đầu bằng điểm trên không gian).
Croad Lang Sơn

1
Điều này không hoạt động đối với các đối số từ khóa như: f(a=1, b=2)(nó chuyển đổi xung quanh)
Att Righ

1
Trong khi câu hỏi là mã C, câu trả lời này hoạt động cho f(a=1, b=2)- emacs.stackexchange.com/a/47930/2418
ideaman42

2

Trong các chế độ sử dụng SMIE, transpose-sexpnên hoạt động chính xác cho trường hợp đó. Họ vẫn sẽ thất bại khi biểu tượng infix (còn gọi là "dấu phân cách") không phải là một ,(hoặc a ;) mà là một từ (ví dụ and).

Vì vậy, ý kiến ​​của tôi là lệnh đó là transpose-sexpvà khi nó không hoạt động chính xác, tôi coi đó là một lỗi (nhưng một lỗi có thể khó và / hoặc mất thời gian để sửa và có mức độ ưu tiên thấp, vì vậy đừng ' t nín thở). Cách khắc phục là bằng cách đặt forward-sexp-functionthành một hàm sẽ biết rằng "sau khi tôi thấy dấu phẩy, tôi chỉ cần nhảy xung quanh toàn bộ đối số".


1
Tôi đoán chế độ java (ví dụ) không sử dụng SMIE? Làm thế nào một người sẽ đi về sửa chữa điều đó?
Phường Samuel Edwin

1
Không, thực sự, các chế độ chính cho các ngôn ngữ giống như C không sử dụng SMIE và không chỉ vì lý do lịch sử: trình phân tích cú pháp của SMIE quá yếu đối với các ngôn ngữ đó. Điều này nói rằng, trình phân tích cú pháp của SMIE sẽ hoạt động tốt nếu bạn muốn trao đổi hai đối số được phân tách bằng dấu phẩy (phần ngữ pháp này đủ đơn giản), vì vậy tôi đoán có thể định cấu hình SMIE và sử dụng nó cho forward-sexp-functionnhưng bạn có để thêm một số mã để chỉ sử dụng SMIE forward-sexp-function trong những trường hợp nó hoạt động đủ tốt, vì trong nhiều trường hợp khác, nó sẽ dẫn đến hành vi khó hiểu.
Stefan
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.