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 / 76956971
Accepted
Antonio Solana
Antonio Solana
Asked: 2023-08-23 05:31:24 +0800 CST2023-08-23 05:31:24 +0800 CST 2023-08-23 05:31:24 +0800 CST

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

  • 772

O comportamento de setfquando combinado com leté confuso para mim. Aqui, setfnão está alterando o segundo valor da minha lista.

(defun to-temp-field (lst)
  (let ((old-value (nth 2 lst))
        (new-value 'hello))
    (progn
      (setf old-value new-value)
      lst)))

Mas se eu fizer sem let, muda ok:

(defun to-temp-field (lst)
  (let ((new-value 'hello))
    (progn
      (setf (nth 2 lst) new-value)
      lst)))

O que está causando esse comportamento?

common-lisp
  • 4 4 respostas
  • 37 Views

4 respostas

  • Voted
  1. Best Answer
    Silvio Mayolo
    2023-08-23T06:22:52+08:002023-08-23T06:22:52+08:00

    Common Lisp é uma das primeiras linguagens a exibir call-by-sharing , um paradigma que agora é extremamente comum em linguagens de programação de tipo dinâmico e de nível superior. Em uma linguagem de chamada por compartilhamento, os valores vinculados a uma variável passam implicitamente por uma espécie de cópia superficial, pegando uma nova versão do ponteiro ou dos dados superiores, mas deixando quaisquer dados aninhados idênticos.

    Em um Lisp, essa distinção é realmente mais fácil de ver. A célula cons de nível superior é copiada, mas quaisquer ponteiros aninhados são deixados de lado.

    (defun to-temp-field (lst)
      (let ((old-value (nth 2 l))
            (new-value 'hello))
        (progn
          (setf old-value new-value)
          lst)))
    

    Aqui, nãoold-value há referência a uma posição na lista. É uma cópia superficial do valor na posição 2 da lista. Conforme indicado na resposta de khachik, na verdade é apenas uma macro muito complicada, e as macros despacham seu comportamento de acordo com a sintaxe que veem. Então, o que é visto como seu primeiro argumento é . Isso está acontecendo em tempo de compilação, então não tem como saber se veio de uma lista, ou de uma chamada, então basta definir a variável local (usando basicamente ).setfsetfold-valuesetfold-valuenthsetq

    Agora, considere seu segundo exemplo,

    (defun to-temp-field (lst)
      (let ((new-value 'hello))
        (progn
          (setf (nth 2 lst) new-value)
          lst)))
    

    Aqui, setfvê (nth 2 lst)como seu primeiro argumento. Esta não é uma variável local. Na verdade, esta é uma expressão semelhante a uma chamada de função. E como podemos ver no hyperspec , nthna verdade tem duas entradas: uma para uma chamada de função normal e outra para quando é usada em setf. Isso porque, assim como podemos definir funções normais como

    (defun nth (n lst)
      ...)
    

    Podemos definir separadamente um setfacessador como

    (defun (setf nth) (n lst)
      ...)
    

    Isso não é pseudocódigo. defunna verdade, aceita (setf whatever)como um nome de função declarado válido e, quando você usa setfem uma chamada de função, a macro procura uma função com esse nome.

    • 1
  2. ad absurdum
    2023-08-23T06:33:43+08:002023-08-23T06:33:43+08:00

    setfpega um lugar e um valor, ou uma série de pares de lugar/valor, e altera o valor do lugar para o novo valor.

    No primeiro exemplo de código old-valuehá uma variável lexical vinculada ao letformulário. Variáveis ​​lexicais são lugares , então chamar (setf old-value new-value)define o valor de old-valueto new-value.

    Os formulários de função também podem ser locais e nthé uma função que pode especificar um local. No segundo exemplo de código, a chamada (setf (nth 2 lst) new-value)define o valor do local (nth 2 lst)como valor new-value.

    Assim no primeiro exemplo o place old-value, uma variável lexical, é atualizado, enquanto no segundo exemplo o place (nth 2 lst), um elemento da lista lsté atualizado.

    Algumas coisas a serem observadas:

    • O segundo exemplo atualiza o terceiro elemento, lstuma vez que as listas são indexadas em zero no Common Lisp.

    • Não há necessidade do prognformulário dentro de let, pois o corpo de um letformulário é implícito progn.

    • A tentativa de modificar um literal causa um comportamento indefinido no Common Lisp, então você não deve chamar a segunda função com uma lista entre aspas como esta: (to-temp-field '(1 2 3))ou com uma variável vinculada a uma lista literal, mas sim assim: (to-temp-field (list 1 2 3))ou com uma variável vinculada a um lista que foi recentemente construída.

    • 1
  3. khachik
    2023-08-23T05:58:30+08:002023-08-23T05:58:30+08:00

    setfé uma macro que, simplesmente, entende o que está sendo definido e chama o conjunto correspondente com base nisso, então (setf old-value new value)simplesmente define o valor, enquanto no segundo caso ele se comporta conforme o esperado.

    • 0
  4. Sylwester
    2023-08-23T07:07:49+08:002023-08-23T07:07:49+08:00

    setfé uma macro e faz coisas completamente diferentes quando o primeiro argumento é um símbolo do que algum acessador para uma estrutura de lista.

    É muito fácil ver que quando você fornece um símbolo, ele faz algo completamente diferente do que se você fornecesse o nthformulário:

    > (get-setf-expansion '(setf (nth 2 lst) new-value))
    (#:TEMP-2859 #:TEMP-2858 #:TEMP-2857) ;
    (2 LST NEW-VALUE) ;
    (#:NEW-2856) ;
    (FUNCALL #'(SETF SYSTEM::%SETNTH) #:NEW-2856 #:TEMP-2859 #:TEMP-2858 #:TEMP-2857) ;
    (SYSTEM::%SETNTH #:TEMP-2859 #:TEMP-2858 #:TEMP-2857)
    
    >  (macroexpand-1 '(setf (nth 2 lst) new-value))
    (SYSTEM::%SETNTH 2 LST NEW-VALUE) ;
    T
    

    A última linha que você vê chama internal function %setnth. Isso é específico da implementação, então o SBCL faz outra coisa. No entanto; Isso claramente busca o local e faz o mesmo que (rplaca (cddr lst) new-value)quando você armazenou em cache o valor de (nth 2 lst)to old-value:

    $(get-setf-expansion '(setf old-value 99))
    (#:TEMP-3005 #:TEMP-3004) ;
    (OLD-VALUE 99) ;
    (#:NEW-3003) ;
    (FUNCALL #'(SETF SETQ) #:NEW-3003 #:TEMP-3005 #:TEMP-3004) ;
    (SETQ #:TEMP-3005 #:TEMP-3004)
    
    > (macroexpand-1 '(setf old-value 99))
    (SETQ OLD-VALUE 99) ;
    T
    

    Isso setqapenas religará a variável local old-valuepara apontar para 99. Você não pode armazenar em cache o local antigo, pois a macro não o compreenderá mais.

    • 0

relate perguntas

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