Qual é a diferença entre "set", "setq" e "setf" no Common Lisp?
Qual é a diferença entre "set", "setq" e "setf" no Common Lisp?
Respostas:
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.
f
realmente 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.
set
é uma função. Assim, ele não conhece o meio ambiente. set
não pode ver a variável lexical. Ele pode definir apenas o valor do símbolo de seu argumento. setq
não está mais "definido entre aspas". O fato de setq
ser uma forma especial, não uma macro mostra isso.
(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
(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 = 5
em C.
setq
é como set
no 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, setf
leva 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.
Você pode usar setf
no lugar de set
ou setq
não vice-versa, pois setf
també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)
setf
tem a capacidade adicional de definir um membro da lista foo
para 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 defvar
se 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))
Pode-se pensar SET
e SETQ
ser 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 SETF
expandisse para SET
e SETQ
.
* (macroexpand '(setf (symbol-value 'a) 10))
(SET 'A 10)
* (macroexpand '(setf a 10))
(SETQ A 10)
Portanto, SET
e SETQ
sã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.
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))