AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 77164863
Accepted
jue
jue
Asked: 2023-09-24 04:53:00 +0800 CST2023-09-24 04:53:00 +0800 CST 2023-09-24 04:53:00 +0800 CST

使用“fboundp”进行“cond”测试时出现警告,为什么?

  • 772

通过以下 Common Lisp 最小示例代码(来自更复杂的代码库),我收到有关行的警告,((fboundp (car ',arg)) ,arg)但我无法解释为什么或如何避免它们。

(defmacro cond-issue (&key (arg '(a b c)))
  (let ((arg-var (gensym "arg-var-")))
    `(let ((,arg-var (cond ((not (listp ',arg)) (list 'arg))
                           ((fboundp (car ',arg)) ,arg)
                           (t ',arg))))
       `(apply #'+ ,,arg-var))))

(cond-issue :arg (e f g))

CCL 抱怨:

;Compiler warnings :
;   In an anonymous lambda form: Undefined function E
;   In an anonymous lambda form: Undeclared free variable F
;   In an anonymous lambda form: Undeclared free variable G

SBCL 抱怨:

; in: cond-issue :arg
;     (E F G)
; 
; caught style-warning:
;   undefined function: common-lisp-user::e
; 
; caught warning:
;   undefined variable: common-lisp-user::f
; 
; caught warning:
;   undefined variable: common-lisp-user::g
; 
; compilation unit finished
;   Undefined function:
;     e
;   Undefined variables:
;     f g
;   caught 2 WARNING conditions
;   caught 1 STYLE-WARNING condition

CLISP 并不抱怨。

使用 SBCL,我尝试了两种方法:编译和评估代码,警告方面没有区别。 [编辑:我从答案中得知我做错了]

这将是第二种cond形式的一个(愚蠢的)示例用例。

(cond-issue :arg (car '(e f g)))
;; ⇒ (apply #'+ e)

CCL 和 SBCL 为何抱怨?
我该如何编写代码来满足CCL和SBCL?

common-lisp
  • 2 2 个回答
  • 48 Views

2 个回答

  • Voted
  1. Best Answer
    ad absurdum
    2023-09-24T13:05:47+08:002023-09-24T13:05:47+08:00

    我不太清楚这个宏的目的,但我认为有关这些警告的性质的问题可以得到解决。我在答案的末尾添加了一些关于可能的重新设计的注释。

    SBCL和CCL抱怨的原因是他们正在编译代码。CLISP repl 解释代码。如果您打开一个新的 SBCL repl 并置于eval解释模式,则可以调用 OP 宏而不会触发警告:

    CL-USER> (setf *evaluator-mode* :interpret)
    :INTERPRET
    CL-USER> (defmacro cond-issue (&key (arg '(a b c)))
      (let ((arg-var (gensym "arg-var-")))
        `(let ((,arg-var (cond ((not (listp ',arg)) (list 'arg))
                               ((fboundp (car ',arg)) ,arg)
                               (t ',arg))))
           `(apply #'+ ,,arg-var))))
    COND-ISSUE
    CL-USER> (cond-issue :arg (e f g))
    (APPLY #'+ (E F G))
    

    回到编译模式后,eval会出现警告:

    CL-USER> (setf *evaluator-mode* :compile)
    :COMPILE
    CL-USER> (cond-issue :arg (e f g))
    ; in: COND-ISSUE :ARG
    ;     (E F G)
    ; 
    ; caught STYLE-WARNING:
    ;   undefined function: COMMON-LISP-USER::E
    ; 
    ; caught WARNING:
    ;   undefined variable: COMMON-LISP-USER::F
    ; 
    ; caught WARNING:
    ;   undefined variable: COMMON-LISP-USER::G
    ; 
    ; compilation unit finished
    ;   Undefined function:
    ;     E
    ;   Undefined variables:
    ;     F G
    ;   caught 2 WARNING conditions
    ;   caught 1 STYLE-WARNING condition
    (APPLY #'+ (E F G))
    CL-USER> (macroexpand-1 '(cond-issue :arg (e f g)))
    (LET ((#:|arg-var-252|
           (COND ((NOT (LISTP '(E F G))) (LIST 'ARG))
                 ((FBOUNDP (CAR '(E F G))) (E F G)) (T '(E F G)))))
      `(APPLY #'+ ,#:|arg-var-252|))
    

    查看上面的宏展开,可以看出要编译的代码的一个分支包含一个带有未定义函数和两个未定义变量的函数调用:((FBOUNDP (CAR '(E F G))) (E F G))。编译器会抱怨,因为它无法编译此代码。我认为这是您真正想要的行为,即您可能不应该尝试规避编译器对未定义函数和变量的警告。

    考虑一些应该编译的代码:

    CL-USER> (cond-issue :arg (list 1 2))
    (APPLY #'+ (1 2))
    CL-USER> (macroexpand-1 '(cond-issue :arg (list 1 2)))
    (LET ((#:|arg-var-258|
           (COND ((NOT (LISTP '(LIST 1 2))) (LIST 'ARG))
                 ((FBOUNDP (CAR '(LIST 1 2))) (LIST 1 2)) (T '(LIST 1 2)))))
      `(APPLY #'+ ,#:|arg-var-258|))
    

    这里的形式(LIST 1 2)已经取代了以前的形式(E F G),这对编译器来说没有问题。请注意,上面的结果形式是(APPLY #'+ (1 2)),这不是合法的 Lisp 代码。我认为其意图是生成(APPLY #'+ '(1 2))。此外,调用(cond-issue :arg 42)产生了(APPLY #'+ (ARG))我怀疑所(APPLY #'+ '(42))期望的结果。cond-issue这是修复这些问题的稍微修改的版本:

    (defmacro cond-issue-fixed (&key (arg '(a b c)))
      (let ((arg-var (gensym "arg-var-")))
        `(let ((,arg-var (cond ((not (listp ',arg)) (list ',arg))
                               ((fboundp (car ',arg)) ,arg)
                               (t ',arg))))
           `(apply #'+ ',,arg-var))))
    

    再说一次,我认为您不应该尝试规避这些编译器警告,但作为一个实验,并本着修补半成品解决方案的精神,您可以包装在一个宏中,首先检查参数cond-issue:

    (defmacro cond-issue-check (&key (arg '(a b c)))
      `(when (every (lambda (item)
                      (or (boundp item)
                          (fboundp item)))
                    (remove-if-not #'symbolp ',arg))
         (cond-issue-fixed :arg ,arg)))
    

    该解决方案有一些注意事项:boundp并fboundp检查全局环境中的绑定,但它们无法检查本地绑定(并且我不确定是否有任何简单的方法可以做到这一点)。此外,包装器将拒绝宏调用,例如(cond-issue-check :arg 42)出现错误。可以增强包装cond-issue-check器以更好地验证输入,但当前的定义应该足以说明这一点。现在是询问宏的目的、是否需要宏并重新审视设计的好时机。

    CL-USER> (cond-issue-check :arg (e f g))
    NIL
    CL-USER> (cond-issue-check :arg (list 1 2))
    (APPLY #'+ '(1 2))
    CL-USER> (let ((a 1) (b 2))
               (cond-issue-check :arg (list a b)))
    NIL
    

    在上面的最后一个示例中,检查包装器无法确定 和 的本地绑定是否a已b绑定。cond-issue当需要本地绑定时,可以调用原始的未包装的,但这意味着可能会因未绑定的函数或变量触发警告。

    无论如何,上述修复不会停止所有缺少定义的警告。一般来说,我不建议尝试抑制这些类型的警告,也不建议使用cond-issue-check. 最好让编译器完成它的工作。

    重新设计宏观

    OP 的目标似乎是采用描述#'+可应用的其他表单的输入表单,并由此创建不需要在当前环境中有效的表单。如果是这种情况,这里有一个替代设计。

    (defmacro cond-something ((&rest args))
      `(if (and (symbolp ',(car `,args))
                (fboundp ',(car `,args)))
           `(apply #'+ ',,args)
           `(apply #'+ ',',args)))
    

    这里,如果输入表单的第一个元素是 fbound 的符号,则#'+生成适用于调用输入表单的结果的表单。#'+否则,将生成适用于所有参数的形式。

    一些调用示例:

    CL-USER> (cond-something (42))
    (APPLY #'+ '(42))
    CL-USER> (eval *)
    42
    CL-USER> (cond-something (list 1 2 3))
    (APPLY #'+ '(1 2 3))
    CL-USER> (eval *)
    6
    CL-USER> (cond-something (mapcar (lambda (x) (* 2 x)) '(1 2 3 4 5)))
    (APPLY #'+ '(2 4 6 8 10))
    CL-USER> (eval *)
    30
    CL-USER> (cond-something (e f g))
    (APPLY #'+ '(E F G))
    CL-USER> (eval *)
    ; Evaluation aborted on #<TYPE-ERROR expected-type: NUMBER datum: E>.
    

    如前所述,fboundp检查全局绑定,但它无法判断词法变量是否绑定到函数,即那些使用fletor创建的变量labels。以下示例展示了如何使用全局函数来cond-something生成预期的输出,以及如何使用局部函数来cond-something生成人们可能天真地期望的代码:

    CL-USER> (defun g (x y) (list (* x y)))
    G
    CL-USER> (cond-something (g 2 3))
    (APPLY #'+ '(6))
    CL-USER> (flet ((f (x y) (list (* x y))))
               (cond-something (f 2 3)))
    (APPLY #'+ '(F 2 3))
    
    • 3
  2. Rainer Joswig
    2023-09-24T17:53:43+08:002023-09-24T17:53:43+08:00

    添加:

    CLISP 也抱怨道:

    CL-USER> (lisp-implementation-type)
    "CLISP"
    
    CL-USER> (defmacro cond-issue (&key (arg '(a b c)))
               (let ((arg-var (gensym "arg-var-")))
                 `(let ((,arg-var (cond ((not (listp ',arg)) (list 'arg))
                                        ((fboundp (car ',arg)) ,arg)
                                        (t ',arg))))
                    `(apply #'+ ,,arg-var))))
    COND-ISSUE
    CL-USER> (defun test () (cond-issue :arg (e f g)))
    TEST
    

    现在让我们编译函数`TEST:

    CL-USER> (compile 'test)
    WARNING: in TEST : Function E is not defined
    WARNING: in TEST : F is neither declared nor bound,
             it will be treated as if it were declared SPECIAL.
    WARNING: in TEST : G is neither declared nor bound,
             it will be treated as if it were declared SPECIAL.
    TEST
    3
    3
    

    然后您需要查看宏展开式:

    CL-USER> (pprint (macroexpand '(cond-issue :arg (e f g))))
    
    (LET
     ((#:|arg-var-16322|
       (COND ((NOT (LISTP '(E F G))) (LIST 'ARG))
        ((FBOUNDP (CAR '(E F G))) (E F G)) (T '(E F G)))))
     (LIST 'APPLY '#'+ #:|arg-var-16322|))
    ; No value
    

    然后看adabsurdum的答案...

    • 2

相关问题

  • 当结构相互指向时无限打印

  • 为什么使用“let”时“setf”不起作用?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    使用 <font color="#xxx"> 突出显示 html 中的代码

    • 2 个回答
  • Marko Smith

    为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类?

    • 1 个回答
  • Marko Smith

    您可以使用花括号初始化列表作为(默认)模板参数吗?

    • 2 个回答
  • Marko Smith

    为什么列表推导式在内部创建一个函数?

    • 1 个回答
  • Marko Smith

    我正在尝试仅使用海龟随机和数学模块来制作吃豆人游戏

    • 1 个回答
  • Marko Smith

    java.lang.NoSuchMethodError: 'void org.openqa.selenium.remote.http.ClientConfig.<init>(java.net.URI, java.time.Duration, java.time.Duratio

    • 3 个回答
  • Marko Smith

    为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)?

    • 4 个回答
  • Marko Smith

    为什么库中不调用全局变量的构造函数?

    • 1 个回答
  • Marko Smith

    std::common_reference_with 在元组上的行为不一致。哪个是对的?

    • 1 个回答
  • Marko Smith

    C++17 中 std::byte 只能按位运算?

    • 1 个回答
  • Martin Hope
    fbrereto 为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类? 2023-12-21 00:31:04 +0800 CST
  • Martin Hope
    比尔盖子 您可以使用花括号初始化列表作为(默认)模板参数吗? 2023-12-17 10:02:06 +0800 CST
  • Martin Hope
    Amir reza Riahi 为什么列表推导式在内部创建一个函数? 2023-11-16 20:53:19 +0800 CST
  • Martin Hope
    Michael A fmt 格式 %H:%M:%S 不带小数 2023-11-11 01:13:05 +0800 CST
  • Martin Hope
    God I Hate Python C++20 的 std::views::filter 未正确过滤视图 2023-08-27 18:40:35 +0800 CST
  • Martin Hope
    LiDa Cute 为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)? 2023-08-24 20:46:59 +0800 CST
  • Martin Hope
    jabaa 为什么库中不调用全局变量的构造函数? 2023-08-18 07:15:20 +0800 CST
  • Martin Hope
    Panagiotis Syskakis std::common_reference_with 在元组上的行为不一致。哪个是对的? 2023-08-17 21:24:06 +0800 CST
  • Martin Hope
    Alex Guteniev 为什么编译器在这里错过矢量化? 2023-08-17 18:58:07 +0800 CST
  • Martin Hope
    wimalopaan C++17 中 std::byte 只能按位运算? 2023-08-17 17:13:58 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve