Ghi đè một chức năng cục bộ, nhưng cho phép các cuộc gọi đến chức năng ban đầu


7

Tính năng tư vấn cho phép sửa đổi hành vi của một chức năng trên toàn cầu. Một định nghĩa lời khuyên có thể thực hiện cuộc gọi đến chức năng ban đầu.

(defadvice foo
  (around foo-bar activate compile)
  "Always set `qux' to t when running `foo'."
  (let ((qux t))
    ad-do-it))

Các clgói cung cấp các fletmacro để ghi đè lên một chức năng tại địa phương.

(defun foo ()
  "global")
(flet ((foo ()
          "local"))
  (some-code-that-calls-foo))

Điều đó không cho phép tham chiếu đến foochức năng ban đầu . Nếu ghi đè cục bộ cần gọi hàm gốc thì sao?

(defun foo ()
  "global")
(flet ((foo ()
          (concat (foo) "+local")))
  ;; this will cause an infinite loop when (foo) is called
  (some-code-that-calls-foo))

Cách tiếp cận đơn giản này không hoạt động, vì lý do chính đáng: (foo)là một cuộc gọi đệ quy đến định nghĩa địa phương.

Cách nào không phức tạp để ghi đè cục bộ một hàm, cho phép gọi hàm gốc từ mã ghi đè?

Ứng dụng: khỉ vá một số mã hiện có, trong trường hợp fookhông nên bật lại trên toàn cầu, nhưng mã cần phải gọi mã gốc. Đây là ví dụ mới nhất mà tôi mong muốn:

(defadvice TeX-master-file
  (around TeX-master-file-indirect-buffer activate compile)
  "Support indirect buffers."
  (flet ((buffer-file-name (&optional buffer)
           (<global buffer-file-name> (buffer-base-buffer buffer))))
      ad-do-it)))

Tôi muốn rebind buffer-file-nametại địa phương, và gọi bản gốc buffer-file-name. Ok, trong trường hợp cụ thể này, có một cách giải quyết, đó là sử dụng buffer-file-namebiến. Nhưng điểm của câu hỏi của tôi ở đây là kỹ thuật chung. Làm cách nào tôi có thể liên kết một hàm (ở đây buffer-file-name) cục bộ nhưng gọi định nghĩa toàn cục từ định nghĩa lại của tôi?

Cái này là của tôi .emacs, cái mà tôi tiếp tục làm việc trong Emacs 19.34, vì vậy các giải pháp yêu cầu Emacs 24.4 đã được đưa ra. Mặc dù vậy, tôi thích các giải pháp đối phó với ràng buộc từ vựng một cách sạch sẽ - nhưng vá khỉ vốn dĩ là về ràng buộc động.


Tôi không chắc rằng cl-letfnó có sẵn trong emacs 24.3 trở về trước, nhưng đây là một câu hỏi và trả lời liên quan: emacs.stackexchange.com/a/16495/221
François Févotte

Câu trả lời:


6

Lưu trữ hàm ban đầu (thu được với symbol-function) trong một biến cục bộ và sử dụng funcallđể gọi đối tượng hàm được lưu trong biến đó. Cồng kềnh, nhưng nó chủ yếu hoạt động.

(defadvice TeX-master-file
  (around TeX-master-file-indirect-buffer activate compile)
  "Support indirect buffers."
  (let ((original-buffer-file-name (symbol-function 'buffer-file-name)))
    (flet ((buffer-file-name (&optional buffer)
             (funcall original-buffer-file-name (buffer-base-buffer buffer))))
        ad-do-it)))

Điều này chủ yếu hoạt động, trong đó nó làm những gì nó phải làm, nhưng nó có thể phá vỡ trong những trường hợp hiếm hoi. Vì Emacs Lisp không có cách nguyên thủy để xác định các vị trí chức năng của biểu tượng cục bộ (chỉ có các vị trí biến đổi, với let), fletđặt các liên kết và khôi phục chúng thông qua unwind-protect. Nếu mã bị chết vì lồng nhau cuộc gọi vượt quá max-lisp-eval-depthhoặc nếu ràng buộc được sửa đổi trong khi thực hiện chức năng này (ví dụ: vì bạn đang gỡ lỗi lời khuyên), có thể không phục hồi vị trí chức năng của biểu tượng. Bạn có thể muốn đề phòng chống vô tình làm mất một số chức năng.

Một phương pháp khác là lưu trữ một bản sao của hàm. Điều này có lợi thế là chức năng ban đầu không bao giờ bị mất.

(fset 'original-buffer-file-name (symbol-function 'buffer-file-name))
(defadvice TeX-master-file
  (around TeX-master-file-indirect-buffer activate compile)
  "Support indirect buffers."
  (flet ((buffer-file-name (&optional buffer)
           (original-buffer-file-name (buffer-base-buffer buffer))))
      ad-do-it)))

Điều này sẽ ổn trong trường hợp cụ thể này, bởi vì buffer-file-namechức năng tích hợp không có khả năng phục hồi, nhưng sẽ không theo dõi các định nghĩa lại của chức năng toàn cầu (ví dụ để thêm lời khuyên cho chức năng đó ).


Bạn có một nadvicecông thức cho cùng?
Kaushal Modi

1
@kaushalmodi Không. Tôi chỉ sử dụng các tính năng mới hơn nếu chúng có lợi ích nhất định. Tôi vẫn sử dụng 24.3 hàng ngày, vì vậy tôi tuân theo cơ chế tư vấn đã hoạt động kể từ Emacs 19 (hoặc sớm hơn?).
Gilles 'SO- ngừng trở nên xấu xa'
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.