gpt4 book ai didi

common-lisp - 获取普通 lisp 中的当前函数名称

转载 作者:行者123 更新时间:2023-12-02 18:02:12 24 4
gpt4 key购买 nike

我知道这超出了标准,但是在特定的实现中,是否有官方方法来获取当前函数名称,比如 SBCL?有时我想将当前函数名称放在日志消息中。我相信,如果有一种一致的方法来获取当前的函数名称,那么维护代码会更容易,至少我不必每次更改函数名称时都更改日志消息。

目前我在 defun 周围使用包装器宏

(defmacro defun2 (name (&rest args) &body body)
`(defun ,name (,@args)
(let ((fun-name ',name))
,@body)))

(defun2 foo (x y)
(format nil "fun ~a: x is ~a, y is ~a~%"
fun-name x y))

但我很想知道是否有更简单的方法。

最佳答案

艰难的道路

假设您想将日志记录添加到您的应用程序中,因此您可以定义自己的小宏和函数,就像您在其他语言中所做的那样:

(defpackage :logging (:use :cl))
(in-package :logging)

为了能够关闭日志记录,或者根据需要更改您的实现,您决定将日志记录封装为宏更好:

(defmacro note (&rest things)
`(note% (first (sb-debug:list-backtrace :count 1))
(list ,@things)))

在上面,我使用 sb-debug:list-backtrace 来捕获当前堆栈帧。它不可移植,当您的函数是 inline 时,您将看不到它的名称,而是调用者的名称。但这是大多数语言中每个日志记录库都接受的可接受的折衷方案。

然后,您可以随意实现它,例如使用条件系统:

(define-condition note (simple-warning)
((timestamp
:initarg :timestamp
:reader note-timestamp)
(origin
:initarg :origin
:reader note-origin)
(data
:initarg :data
:reader note-data))
(:report (lambda (c s)
(format s
"~d ~a ~@<~s~:>"
(note-timestamp c)
(note-origin c)
(note-data c)))))

此处的 note% 函数发出警告,但想法是在更高级别捕获所有这些条件并将它们写在其他地方。如果没有处理程序,SBCL 会将其打印在 REPL 上,这样也可以:

(defun note% (origin data)
(warn 'note
:timestamp (local-time:now)
:origin origin
:data data))

最后,您可以按如下方式进行测试:

(defun test (in)
(flet ((y (x) (note :x x)))
(note :before :in in)
(y 6)
(note :after)))

例如:

(test 15)
WARNING: 2022-10-19T15:17:05.297198Z (TEST 15) (:BEFORE :IN 15)
WARNING: 2022-10-19T15:17:05.297603Z ((FLET Y IN TEST) 6) (:X 6)
WARNING: 2022-10-19T15:17:05.297706Z (TEST 15) (:AFTER)

简单的方法

您可能不想执行上述所有操作,而是希望在将代码提供给用户时激活跟踪。您选择要跟踪的功能,并将 *TRACE-OUTPUT* 重定向到您的日志(或使用自定义报告功能,见下文)。

SBCL 实现假定字符串参数表示包,但这是不可移植的。然而,这种不可移植性的影响较小,因为它只在一个地方完成,不会触及所有代码。如果需要,始终可以列出所有符号并显式跟踪它们,或者使用阅读器宏以适合您的方式调用特定于实现的 TRACE

例如,使用 SBCL,让我们跟踪我新创建的包中的所有符号:

(trace "LOGGING")

并运行相同的测试:

(test 15)
0: (LOGGING::TEST 15)
1: (LOGGING::NOTE% (LOGGING::TEST 15) (:BEFORE :IN 15))
2: (LOGGING::NOTE-TIMESTAMP #<LOGGING::NOTE {100EC7CC33}>)
2: NOTE-TIMESTAMP returned @2022-10-19T15:22:05.315488Z
2: (LOGGING::NOTE-ORIGIN #<LOGGING::NOTE {100EC7CC33}>)
2: NOTE-ORIGIN returned (TEST 15)
2: (LOGGING::NOTE-DATA #<LOGGING::NOTE {100EC7CC33}>)
2: NOTE-DATA returned (:BEFORE :IN 15)
WARNING: 2022-10-19T15:22:05.315488Z (TEST 15) (:BEFORE :IN 15)
1: NOTE% returned NIL
1: (LOGGING::NOTE% ((FLET LOGGING::Y :IN LOGGING::TEST) 6) (:X 6))
2: (LOGGING::NOTE-TIMESTAMP #<LOGGING::NOTE {100EC9DBB3}>)
2: NOTE-TIMESTAMP returned @2022-10-19T15:22:05.319801Z
2: (LOGGING::NOTE-ORIGIN #<LOGGING::NOTE {100EC9DBB3}>)
2: NOTE-ORIGIN returned ((FLET Y :IN TEST) 6)
2: (LOGGING::NOTE-DATA #<LOGGING::NOTE {100EC9DBB3}>)
2: NOTE-DATA returned (:X 6)
WARNING: 2022-10-19T15:22:05.319801Z ((FLET Y IN TEST) 6) (:X 6)
1: NOTE% returned NIL
1: (LOGGING::NOTE% (LOGGING::TEST 15) (:AFTER))
2: (LOGGING::NOTE-TIMESTAMP #<LOGGING::NOTE {100ECAE773}>)
2: NOTE-TIMESTAMP returned @2022-10-19T15:22:05.323732Z
2: (LOGGING::NOTE-ORIGIN #<LOGGING::NOTE {100ECAE773}>)
2: NOTE-ORIGIN returned (TEST 15)
2: (LOGGING::NOTE-DATA #<LOGGING::NOTE {100ECAE773}>)
2: NOTE-DATA returned (:AFTER)
WARNING: 2022-10-19T15:22:05.323732Z (TEST 15) (:AFTER)
1: NOTE% returned NIL
0: TEST returned NIL

在不更改代码的情况下,我现在可以准确地看到发生了什么。事实上,我根本不需要实现日志记录功能。

某些实现允许您配置跟踪的执行方式,因此如果您想添加时间戳,您也应该能够这样做。在 SBCL 中,这是接受函数名称的 :report 选项。在这里,我使用自定义 my-report 函数拦截每个条目:

(trace "LOGGING" :report my-report)

(defun my-report (&rest args)
(fresh-line *trace-output*)
(print (list* (local-time:now) args) *trace-output*))

例如:

(test 15)
(@2022-10-19T15:34:58.543114Z 0 LOGGING::TEST :ENTER #<SB-DI::COMPILED-FRAME SB-INT:SIMPLE-EVAL-IN-LEXENV> (15))

(@2022-10-19T15:34:58.552193Z 1 LOGGING::NOTE% :ENTER #<SB-DI::COMPILED-FRAME LOGGING::TEST> ((LOGGING::TEST 15) (:BEFORE :IN 15)))

(@2022-10-19T15:34:58.552359Z 2 LOGGING::NOTE-TIMESTAMP :ENTER #<SB-DI::COMPILED-FRAME (SB-KERNEL::CONDITION-REPORT LOGGING::NOTE)> (#<LOGGING::NOTE {100F5E7F23}>))

(@2022-10-19T15:34:58.552461Z 2 NOTE-TIMESTAMP :EXIT
#<SB-DI::COMPILED-FRAME (SB-KERNEL::CONDITION-REPORT NOTE)>
(@2022-10-19T15:34:58.552306Z))
....

关于common-lisp - 获取普通 lisp 中的当前函数名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74122204/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com