Các chức năng có thể truy cập tên của họ?


25

Trong C có biến ma thuật __func__chứa tên hàm hiện tại. Trong Bash, có một mảng FUNCNAMEchứa tên của tất cả các hàm trong ngăn xếp cuộc gọi !!!

Có một điều tương tự trong Emacs Lisp? Hoặc bất kỳ cách đơn giản để một chức năng có quyền truy cập vào tên của nó?

Tôi chưa tìm thấy câu trả lời nào trong hướng dẫn sử dụng Emacs Lisp (chương 12 về Hàm hoặc chỉ mục các biến và hàm và ..)


2
Bạn thực sự cần điều đó để làm gì?
lunaryorn

1
Đây không hẳn là một biến số ma thuật - đó là một định nghĩa tiền xử lý được chèn bởi một số trình biên dịch.
Sean Allred

Câu trả lời:


6

Đối với các hàm tương tác, tức là các lệnh, bạn có thể sử dụng biến this-commandhoặc an toàn hơn real-this-command. Sự khác biệt là khi bạn viết các hàm của riêng mình, bạn có thể thay đổi rõ ràng giá trị của this-command. Chủ yếu là điều này được thực hiện để chơi các thủ thuật last-commandliên quan đến các lệnh lặp lại. Bạn không nên (không thể?) Làm điều này với real-this-command. Nó sẽ luôn là tên của lệnh hiện tại.

Tôi không biết tương đương với các chức năng không tương tác.


3
Điều quan trọng cần lưu ý là this-commandvà hoàn toàn real-last-commandkhông thích __func__. Ví dụ, nếu lệnh A gọi lệnh B this-commandsẽ in ra lệnh này sẽ in lệnh A, không phải B, điều này hoàn toàn không hoạt động đối với các chức năng.
Jordon Biondo

1
@JordonBiondo Đồng ý. Tôi đã lưu ý rằng nó không hoạt động cho các chức năng. Phs đã không cung cấp cho chúng tôi bối cảnh của anh ấy, vì vậy tôi đoán rằng điều này có thể đủ tốt cho ứng dụng của anh ấy. Câu trả lời của bạn mạnh mẽ hơn, không có câu hỏi.
Tyler

22

Cập nhật câu trả lời với tra cứu thời gian mở rộng:

Tôi đã nói trong câu trả lời ban đầu của mình rằng có thể có một cách để làm điều này vào thời gian mở rộng / biên dịch thay vì thời gian chạy để mang lại hiệu suất tốt hơn và cuối cùng tôi đã thực hiện điều đó hôm nay trong khi thực hiện câu trả lời của mình cho câu hỏi này: Làm thế nào tôi có thể xác định được chức năng nào gọi là tương tác trong ngăn xếp?

Đây là một hàm mang lại tất cả các khung backtrace hiện tại

(defun call-stack ()
  "Return the current call stack frames."
  (let ((frames)
        (frame)
        (index 5))
    (while (setq frame (backtrace-frame index))
      (push frame frames)
      (incf index))
    (remove-if-not 'car frames)))

Sử dụng điều đó trong một macro, chúng ta có thể tra cứu ngăn xếp mở rộng để xem định nghĩa hàm nào đang được mở rộng tại thời điểm đó và đặt giá trị đó ngay trong mã.

Đây là chức năng để thực hiện việc mở rộng:

(defmacro compile-time-function-name ()
  "Get the name of calling function at expansion time."
  (symbol-name
   (cadadr
    (third
     (find-if (lambda (frame) (ignore-errors (equal (car (third frame)) 'defalias)))
              (reverse (call-stack)))))))

Đây là hành động.

(defun my-test-function ()
  (message "This function is named '%s'" (compile-time-function-name)))

(symbol-function 'my-test-function)
;; you can see the function body contains the name, not a lookup
(lambda nil (message "This function is named '%s'" "my-test-function"))

(my-test-function)
;; results in:
"This function is named 'my-test-function'"

Câu trả lời gốc:

Bạn có thể sử dụng backtrace-frameđể tra cứu ngăn xếp cho đến khi bạn thấy khung đại diện cho một lệnh gọi hàm trực tiếp và lấy tên từ đó.

(defun get-current-func-name ()
  "Get the symbol of the function this function is called from."
  ;; 5 is the magic number that makes us look 
  ;; above this function
  (let* ((index 5)
         (frame (backtrace-frame index)))
    ;; from what I can tell, top level function call frames
    ;; start with t and the second value is the symbol of the function
    (while (not (equal t (first frame)))
      (setq frame (backtrace-frame (incf index))))
    (second frame)))

(defun my-function ()
  ;; here's the call inside my-function
  (when t (progn (or (and (get-current-func-name))))))

(defun my-other-function ()
  ;; we should expect the return value of this function
  ;; to be the return value of my-function which is the
  ;; symbol my-function
  (my-function))

(my-other-function) ;; => 'my-function

Ở đây tôi đang thực hiện tra cứu tên hàm trong thời gian chạy mặc dù có thể thực hiện điều này ở một macro mở rộng trực tiếp vào biểu tượng hàm sẽ hiệu quả hơn cho các cuộc gọi lặp lại và elisp được biên dịch.

Tôi đã tìm thấy thông tin này trong khi cố gắng viết một loại logger chức năng cho elisp có thể tìm thấy ở đây dưới dạng không đầy đủ, nhưng nó có thể hữu ích cho bạn. https://github.com/jordonbiondo/call-log


Cảm ơn mã. Điều này giải quyết vấn đề của tôi và tôi sẽ chấp nhận nó trong một vài ngày trừ khi ai đó đưa cho chúng tôi một loại biến "tên hàm hiện tại" nào đó là một phần của trình gỡ lỗi mà bạn gợi ý.
phs

Nhân tiện, nó dường như không hoạt động khi a defunđược bao quanh bởi eval-and-compile, tức là nó trở lại nil.
Alexander Shukaev
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.