AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 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

Avisos em testes `cond` com `fboundp`, por quê?

  • 772

Seguindo o código de exemplo mínimo do Common Lisp (de uma base de código muito mais complexa), recebo avisos sobre line ((fboundp (car ',arg)) ,arg)e não consigo explicar por que ou como evitá-los.

(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 reclama:

;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 reclama:

; 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 não reclama.

Com o SBCL tentei as duas coisas: compilar e avaliar o código, sem diferença quanto aos avisos. [editar: aprendi com as respostas que fiz isso errado]

Este seria um exemplo de caso de uso (bobo) para o segundo condformulário.

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

Qual a explicação, por que a CCL e a SBCL estão reclamando?
E como posso escrever o código para satisfazer CCL e SBCL?

common-lisp
  • 2 2 respostas
  • 48 Views

2 respostas

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

    O propósito desta macro não está muito claro para mim, mas acho que a questão sobre a natureza desses avisos pode ser abordada. Adicionei algumas notas sobre um possível redesenho no final da resposta.

    A razão pela qual SBCL e CCL estão reclamando é que estão compilando código. A resposta CLISP interpreta o código. Se você abrir uma nova replicação SBCL e colocá-la evalno modo interpretado, a macro OP poderá ser invocada sem disparar avisos:

    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))
    

    Depois de evalvoltar ao modo compilado, os avisos aparecem:

    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|))
    

    Observando a expansão da macro acima, pode-se observar que um ramo do código a ser compilado contém uma chamada de função com uma função indefinida e duas variáveis ​​indefinidas: ((FBOUNDP (CAR '(E F G))) (E F G)). O compilador está reclamando porque não consegue compilar este código. Eu acho que esse é o comportamento que você realmente deseja, ou seja, você provavelmente não deveria tentar contornar os avisos do compilador para funções e variáveis ​​indefinidas.

    Considere algum código que deve ser compilado:

    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|))
    

    Aqui o formulário (LIST 1 2)substituiu o anterior (E F G)e isso não representa nenhum problema para o compilador. Observe que o formulário resultante acima é (APPLY #'+ (1 2))e este não é um código lisp legal. Acho que a intenção era gerar (APPLY #'+ '(1 2))em vez disso. Além disso, a invocação (cond-issue :arg 42)produz resultados (APPLY #'+ (ARG))onde suspeito que (APPLY #'+ '(42))era desejado. Aqui está uma versão ligeiramente modificada que cond-issuecorrige esses problemas:

    (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))))
    

    Novamente, acho que você não deveria tentar contornar esses avisos do compilador, mas como um experimento, e no espírito de consertar uma solução incompleta, você poderia agrupar uma cond-issuemacro que verifica os argumentos primeiro:

    (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)))
    

    Esta solução tem algumas ressalvas: boundpe fboundpverifica ligações no ambiente global, mas não pode inspecionar ligações locais (e não tenho certeza se existe uma maneira simples de fazer isso). Além disso, o wrapper rejeitará invocações de macro como acontece (cond-issue-check :arg 42)com um erro. O cond-issue-checkwrapper poderia ser aumentado para validar melhor a entrada, mas a definição atual deve ser suficiente para deixar claro. Agora seria um bom momento para questionar o propósito da macro, se ela precisa ser uma macro, e para revisitar o design.

    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
    

    No exemplo final acima, o wrapper de verificação não pode determinar se as ligações locais para ae bestão vinculadas. O original, desembrulhado, cond-issuepode ser chamado quando ligações locais são necessárias, mas isso significa que avisos podem ser acionados para funções ou variáveis ​​não vinculadas.

    De qualquer forma, a correção acima não interromperá todos os avisos de definições ausentes. Em geral, eu não recomendaria tentar suprimir esses tipos de avisos e não recomendaria usar cond-issue-check. Provavelmente é melhor deixar o compilador fazer o seu trabalho.

    Redesenhando a Macro

    Parece que o objetivo do OP é tomar como entrada formulários que descrevam outros formulários aos quais #'+podem ser aplicados e, a partir disso, criar formulários que não precisam ser válidos em seu ambiente atual. Se for esse o caso, aqui está um design alternativo.

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

    Aqui, se o primeiro elemento de um formulário de entrada for um símbolo fbound, #'+será gerado um formulário que se aplica ao resultado da chamada do formulário de entrada. Caso contrário, será gerado um formulário que se aplica #'+a todos os argumentos.

    Alguns exemplos de invocações:

    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>.
    

    Como mencionado anteriormente, fboundpinspeciona ligações globais, mas não consegue dizer se as variáveis ​​lexicais estão vinculadas a funções, ou seja, aquelas criadas com fletou labels. Aqui estão exemplos que mostram o uso de uma função global para a qual cond-somethinggera a saída esperada e o uso de uma função local para a qual cond-somethingnão geraria o código que se poderia ingenuamente esperar:

    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

    Adição:

    CLISP também reclama:

    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
    

    Agora vamos compilar a função `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
    

    Você então precisa olhar para a macroexpansão:

    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
    

    Então veja a resposta por ad absurdum ...

    • 2

relate perguntas

  • Impressão infinita quando estruturas apontam umas para as outras

  • Por que “setf” não funciona ao usar “let”?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    destaque o código em HTML usando <font color="#xxx">

    • 2 respostas
  • Marko Smith

    Por que a resolução de sobrecarga prefere std::nullptr_t a uma classe ao passar {}?

    • 1 respostas
  • Marko Smith

    Você pode usar uma lista de inicialização com chaves como argumento de modelo (padrão)?

    • 2 respostas
  • Marko Smith

    Por que as compreensões de lista criam uma função internamente?

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Marko Smith

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

    • 3 respostas
  • Marko Smith

    Por que 'char -> int' é promoção, mas 'char -> short' é conversão (mas não promoção)?

    • 4 respostas
  • Marko Smith

    Por que o construtor de uma variável global não é chamado em uma biblioteca?

    • 1 respostas
  • Marko Smith

    Comportamento inconsistente de std::common_reference_with em tuplas. Qual é correto?

    • 1 respostas
  • Marko Smith

    Somente operações bit a bit para std::byte em C++ 17?

    • 1 respostas
  • Martin Hope
    fbrereto Por que a resolução de sobrecarga prefere std::nullptr_t a uma classe ao passar {}? 2023-12-21 00:31:04 +0800 CST
  • Martin Hope
    比尔盖子 Você pode usar uma lista de inicialização com chaves como argumento de modelo (padrão)? 2023-12-17 10:02:06 +0800 CST
  • Martin Hope
    Amir reza Riahi Por que as compreensões de lista criam uma função internamente? 2023-11-16 20:53:19 +0800 CST
  • Martin Hope
    Michael A formato fmt %H:%M:%S sem decimais 2023-11-11 01:13:05 +0800 CST
  • Martin Hope
    God I Hate Python std::views::filter do C++20 não filtrando a visualização corretamente 2023-08-27 18:40:35 +0800 CST
  • Martin Hope
    LiDa Cute Por que 'char -> int' é promoção, mas 'char -> short' é conversão (mas não promoção)? 2023-08-24 20:46:59 +0800 CST
  • Martin Hope
    jabaa Por que o construtor de uma variável global não é chamado em uma biblioteca? 2023-08-18 07:15:20 +0800 CST
  • Martin Hope
    Panagiotis Syskakis Comportamento inconsistente de std::common_reference_with em tuplas. Qual é correto? 2023-08-17 21:24:06 +0800 CST
  • Martin Hope
    Alex Guteniev Por que os compiladores perdem a vetorização aqui? 2023-08-17 18:58:07 +0800 CST
  • Martin Hope
    wimalopaan Somente operações bit a bit para std::byte em C++ 17? 2023-08-17 17:13:58 +0800 CST

Hot tag

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

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve