Diferença entre `set`,` setq` e `setf` no Common Lisp?

166

Qual é a diferença entre "set", "setq" e "setf" no Common Lisp?

Richard Hoskins
fonte
9
O comportamento deles é respondido muito bem nas respostas, mas a resposta aceita tem uma etimologia possivelmente equivocada para o "f" em "setf". A resposta para O que significa f em setf? diz que é para "função" e fornece referências para fazer backup.
Joshua Taylor

Respostas:

169

Originalmente, no Lisp, não havia variáveis ​​lexicais - apenas dinâmicas. E não havia SETQ ou SETF, apenas a função SET.

O que agora está escrito como:

(setf (symbol-value '*foo*) 42)

foi escrito como:

(set (quote *foo*) 42)

que foi abreviado para SETQ (SET Quoted):

(setq *foo* 42)

Então as variáveis ​​lexicais aconteceram, e o SETQ passou a ser usado para atribuição a elas também - portanto, não era mais um invólucro simples em torno do SET.

Mais tarde, alguém inventou o SETF (campo SET) como uma maneira genérica de atribuir valores a estruturas de dados, para espelhar os valores l de outras linguagens:

x.car := 42;

seria escrito como

(setf (car x) 42)

Por simetria e generalidade, o SETF também forneceu a funcionalidade do SETQ. Nesse ponto, seria correto dizer que SETQ era uma primitiva de baixo nível e SETF uma operação de alto nível.

Então macros de símbolos aconteceram. Para que as macros de símbolo pudessem funcionar de forma transparente, percebeu-se que SETQ teria que agir como SETF se a "variável" atribuída a ela fosse realmente uma macro de símbolo:

(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))

foo => 42

(setq foo 13)

foo => 13

*hidden* => (13 . 42)

Portanto, chegamos hoje: SET e SETQ são restos atrofiados de dialetos mais antigos e provavelmente serão inicializados por eventuais sucessores do Common Lisp.

programador de pilha
fonte
45
Lisp comum sempre teve variáveis ​​lexicais. Você deve estar falando sobre alguns Lisp antes do Common Lisp.
Rainer Joswig
4
Se for necessário inicializar o SET e o SETQ a partir de um sucessor do Common Lisp, eles precisarão ser substituídos. Seu uso no código de alto nível é limitado, mas o código de baixo nível (por exemplo, o código no qual SETF é implementado) precisa deles.
Svante
13
existe uma razão para você escolher 'carro' como um campo em vez de algo que pode ficar confuso com a função do carro?
11118 drudru
9
Esta resposta para O que significa f em setf? afirma que frealmente significa função , não campo (ou forma , para esse assunto) e fornece referências; portanto, embora o campo setf para faça algum sentido, parece que pode não estar correto.
Joshua Taylor
1
Resumo: seté uma função. Assim, ele não conhece o meio ambiente. setnão pode ver a variável lexical. Ele pode definir apenas o valor do símbolo de seu argumento. setqnão está mais "definido entre aspas". O fato de setqser uma forma especial, não uma macro mostra isso.
KIM Taegyoon
142
(set ls '(1 2 3 4)) => Error - ls has no value

(set 'ls '(1 2 3 4)) => OK

(setq ls '(1 2 3 4)) => OK - make ls to (quote ls) and then have the usual set

(setf ls '(1 2 3 4)) => OK - same as setq so far BUT

(setf (car ls) 10) => Makes ls '(10 2 3 4) - not duplicated by setq/set
Sourav
fonte
12
Considero sua resposta mais clara do que a mais votada. Muito obrigado.
CDR
2
@Sourav, NUNCA use a letra "l" (ell) como uma variável ou símbolo no código de exemplo. É muito difícil de distinguir visualmente a partir do algarismo 1.
DavidBooth
Não, ainda não entendo como (car ls) pode ser um valor l ou não. Você entende como traduzir o CLisp em C? e como escrever um intérprete CLisp?
executa
@ user1952009 clisp é uma implementação Common Lisp. Se você quiser se referir ao idioma em si, CL é a abreviação mais usada.
ssice 18/05
2
@ user1952009 After (setq ls '(((1)))), (setf (car (car (car ls))) 5)é um comportamento indefinido, porque o valor de lsé constante (como modificar uma literal de cadeia de caracteres em C). Depois (setq ls (list (list (list 1)))), (setf (car (car (car ls))) 5)funciona exatamente como ls->val->val->val = 5em C.
kyle
21

setqé como setno primeiro argumento citado - (set 'foo '(bar baz))é como (setq foo '(bar baz)). setf, por outro lado, é realmente sutil - é como um "indireto". Sugiro http://www.nano.com/lisp/cmucl-tutorials/LISP-tutorial-16.html como uma maneira melhor de começar entendê-lo do que qualquer resposta aqui pode dar ... em suma, no entanto, setfleva a primeiro argumento como uma "referência", de modo que, por exemplo (aref myarray 3), funcione (como o primeiro argumento setf) para definir um item dentro de uma matriz.

Alex Martelli
fonte
1
Faz mais sentido para o nome setq. Fácil de lembrar. Obrigado.
CDR
17

Você pode usar setfno lugar de setou setqnão vice-versa, pois setftambém pode definir o valor dos elementos individuais de uma variável se a variável tiver elementos individuais. Veja os exemplos abaixo:

Todos os quatro exemplos atribuirão a lista (1, 2, 3) à variável denominada foo.

(set (quote foo) (list 1 2 3))    ;foo => (1 2 3)
(1 2 3)

(set 'foo '(1 2 3))   ;foo => (1 2 3) same function, simpler expression
(1 2 3)

(setq foo '(1 2 3))   ;foo => (1 2 3) similar function, different syntax
(1 2 3)

(setf foo '(1 2 3))   ;foo => (1 2 3) more capable function
(1 2 3)

setftem a capacidade adicional de definir um membro da lista foopara um novo valor.

foo                   ;foo => (1 2 3) as defined above
(1 2 3)

(car foo)             ;the first item in foo is 1
1

(setf (car foo) 4)    ;set or setq will fail since (car foo) is not a symbol
4

foo                   ;the fist item in foo was set to 4 by setf
(4 2 3)

No entanto, você pode definir uma macro de símbolo que represente um único item dentro de foo

(define-symbol-macro foo-car (car foo))    ; assumes FOO => (1 2 3)
FOO-CAR

foo-car               ;foo-car is now a symbol for the 1st item in foo
1

(setq foo-car 4)      ;set or setq can set the symbol foo-car 
4

foo                   ;Lisp macros are so cool
(4 2 3)

Você pode usar defvarse você ainda não definiu a variável e não deseja atribuir um valor até mais tarde no seu código.

(defvar foo2)
(define-symbol-macro foo-car (car foo2))
dansalmo
fonte
13

Pode-se pensar SETe SETQser construções de baixo nível.

  • SET pode definir o valor dos símbolos.

  • SETQ pode definir o valor das variáveis.

Então SETFé uma macro, que fornece muitos tipos de configurações: símbolos, variáveis, elementos de matriz, slots de instância, ...

Para símbolos e variáveis, pode-se pensar como se se SETFexpandisse para SETe SETQ.

* (macroexpand '(setf (symbol-value 'a) 10))

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

Portanto, SETe SETQsão usados ​​para implementar algumas das funcionalidades de SETF, que é a construção mais geral. Algumas das outras respostas contam a história um pouco mais complexa, quando levamos em consideração as macros de símbolos.

Rainer Joswig
fonte
4

Gostaria de acrescentar às respostas anteriores que setf é macro que chama função específica dependendo do que foi passado como seu primeiro argumento. Compare os resultados da expansão macro de setf com diferentes tipos de argumentos:

(macroexpand '(setf a 1))

(macroexpand '(setf (car (list 3 2 1)) 1))

(macroexpand '(setf (aref #(3 2 1) 0) 1))

Para alguns tipos de argumentos, a "função setf" será chamada:

(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))
Filipp
fonte