Existem várias razões pelas quais não se deve usar EVAL
.
A principal razão para iniciantes é: você não precisa disso.
Exemplo (assumindo Common Lisp):
AVALUE uma expressão com diferentes operadores:
(let ((ops '(+ *)))
(dolist (op ops)
(print (eval (list op 1 2 3)))))
Isso é melhor escrito como:
(let ((ops '(+ *)))
(dolist (op ops)
(print (funcall op 1 2 3))))
Existem muitos exemplos em que os iniciantes que aprendem o Lisp acham que precisam EVAL
, mas não precisam - já que as expressões são avaliadas e também é possível avaliar a parte da função. Na maioria das vezes, o uso de EVAL
mostra falta de entendimento do avaliador.
É o mesmo problema com macros. Geralmente, os iniciantes escrevem macros, onde devem escrever funções - sem entender para que servem as macros e sem entender que uma função já faz o trabalho.
Geralmente, é a ferramenta errada para o trabalho usar EVAL
e geralmente indica que o iniciante não entende as regras usuais de avaliação do Lisp.
Se você acha que precisa EVAL
, em seguida, verificar se algo assim FUNCALL
, REDUCE
ou APPLY
poderia ser usado em seu lugar.
FUNCALL
- chame uma função com argumentos: (funcall '+ 1 2 3)
REDUCE
- chame uma função em uma lista de valores e combine os resultados: (reduce '+ '(1 2 3))
APPLY
- chamar uma função com uma lista de argumentos: (apply '+ '(1 2 3))
.
P: Eu realmente preciso de avaliação ou o compilador / avaliador já é o que realmente quero?
Os principais motivos a serem evitados EVAL
para usuários um pouco mais avançados:
você deseja garantir que seu código seja compilado, porque o compilador pode verificar se há muitos problemas e gerar um código mais rápido, às vezes MUITO MUITO MUITO (o fator 1000 ;-)) código mais rápido
o código que é construído e precisa ser avaliado não pode ser compilado o mais cedo possível.
avaliação de entrada arbitrária do usuário abre problemas de segurança
algum uso da avaliação EVAL
pode ocorrer no momento errado e criar problemas de compilação
Para explicar o último ponto com um exemplo simplificado:
(defmacro foo (a b)
(list (if (eql a 3) 'sin 'cos) b))
Então, talvez eu queira escrever uma macro que, com base no primeiro parâmetro, use SIN
ou COS
.
(foo 3 4)
faz (sin 4)
e (foo 1 4)
faz (cos 4)
.
Agora podemos ter:
(foo (+ 2 1) 4)
Isso não fornece o resultado desejado.
Pode-se reparar a macro FOO
EVALuating a variável:
(defmacro foo (a b)
(list (if (eql (eval a) 3) 'sin 'cos) b))
(foo (+ 2 1) 4)
Mas isso ainda não funciona:
(defun bar (a b)
(foo a b))
O valor da variável simplesmente não é conhecido no momento da compilação.
Um motivo geral importante a ser evitado EVAL
: geralmente é usado para hacks feios.