Eu sei que os programadores de Lisp e Scheme geralmente dizem que isso eval
deve ser evitado, a menos que seja estritamente necessário. Eu vi a mesma recomendação para várias linguagens de programação, mas ainda não vi uma lista de argumentos claros contra o uso de eval
. Onde posso encontrar uma conta dos possíveis problemas do uso eval
?
Por exemplo, conheço os problemas da GOTO
programação processual (torna os programas ilegíveis e difíceis de manter, dificulta a localização de problemas de segurança etc.), mas nunca vi argumentos contra isso eval
.
Curiosamente, os mesmos argumentos contra GOTO
devem ser válidos contra continuações, mas vejo que os Schemers, por exemplo, não dirão que continuações são "más" - você deve ter cuidado ao usá-las. Eles são muito mais propensos a desaprovar o código usando do eval
que o código usando continuações (tanto quanto eu posso ver - eu posso estar errado).
Respostas:
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:
Isso é melhor escrito como:
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 deEVAL
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 assimFUNCALL
,REDUCE
ouAPPLY
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çãoPara explicar o último ponto com um exemplo simplificado:
Então, talvez eu queira escrever uma macro que, com base no primeiro parâmetro, use
SIN
ouCOS
.(foo 3 4)
faz(sin 4)
e(foo 1 4)
faz(cos 4)
.Agora podemos ter:
Isso não fornece o resultado desejado.
Pode-se reparar a macro
FOO
EVALuating a variável:Mas isso ainda não funciona:
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.fonte
eval
simplesmente porque não sabem que há um recurso específico de idioma ou biblioteca para fazer o que querem. Exemplo semelhante de JS: desejo obter uma propriedade de um objeto usando um nome dinâmico, então escrevo:eval("obj.+" + propName)
quando poderia ter escritoobj[propName]
."obj.+"
:? A última vez que verifiquei,+
não é válida ao usar referências de ponto em JS.eval
(em qualquer idioma) não é mau da mesma maneira que uma serra elétrica não é má. É uma ferramenta. Por acaso, é uma ferramenta poderosa que, quando mal utilizada, pode cortar membros e eviscerar (metaforicamente falando), mas o mesmo pode ser dito para muitas ferramentas na caixa de ferramentas de um programador, incluindo:goto
e amigosSe você precisar usar alguma dessas ferramentas poderosas e potencialmente perigosas, pergunte a si mesmo três vezes "por quê?" em uma corrente. Por exemplo:
Se você chegar ao final dessa cadeia e a ferramenta ainda parecer que é a coisa certa a fazer, faça-o. Documente o inferno com isso. Teste o inferno com isso. Verifique novamente a correção e a segurança repetidamente. Mas faça isso.
fonte
Eval está bem, desde que você saiba EXATAMENTE o que está acontecendo. Qualquer entrada do usuário deve ser verificada e validada e tudo mais. Se você não sabe como ter 100% de certeza, não faça isso.
Basicamente, um usuário pode digitar qualquer código para o idioma em questão e ele será executado. Você pode imaginar por si mesmo quanto dano ele pode causar.
fonte
"Quando devo usar
eval
?" pode ser uma pergunta melhor.A resposta curta é "quando seu programa pretende gravar outro programa em tempo de execução e depois executá-lo". A programação genética é um exemplo de uma situação em que provavelmente faz sentido usar
eval
.fonte
IMO, esta questão não é específica para LISP . Aqui está uma resposta sobre a mesma pergunta para PHP e se aplica a LISP, Ruby e outra outra linguagem que possui uma avaliação:
Retirado daqui .
Eu acho que a peça complicada é um ponto incrível. A obsessão pelo código golf e pelo código conciso sempre resultou em um código "inteligente" (para o qual as avaliações são uma ótima ferramenta). Mas você deve escrever seu código para facilitar a leitura, IMO, para não demonstrar que é esperto e não economizar papel (você não o imprimirá de qualquer maneira).
Então, no LISP, há algum problema relacionado ao contexto em que o eval é executado, para que o código não confiável possa ter acesso a mais coisas; esse problema parece ser comum de qualquer maneira.
fonte
Houve muitas ótimas respostas, mas aqui está outro exemplo de Matthew Flatt, um dos implementadores do Racket:
http://blog.racket-lang.org/2011/10/on-eval-in-dynamic-languages-generally.html
Ele faz muitos dos pontos que já foram abordados, mas algumas pessoas podem achar sua opinião interessante, no entanto.
Resumo: O contexto em que é usado afeta o resultado da avaliação, mas geralmente não é considerado pelos programadores, levando a resultados inesperados.
fonte
A resposta canônica é ficar longe. O que eu acho estranho, porque é um primitivo, e dos sete primitivos (os outros são contras, carro, cdr, if, eq e citação), fica de longe a menor quantidade de uso e amor.
De On Lisp : "Normalmente, telefonar para eval explicitamente é como comprar algo em uma loja de presentes do aeroporto. Depois de esperar até o último momento, é preciso pagar preços altos por uma seleção limitada de mercadorias de segunda categoria".
Então, quando uso eval? Um uso normal é ter um REPL dentro do seu REPL, avaliando
(loop (print (eval (read))))
. Todo mundo está bem com esse uso.Mas você também pode definir funções em termos de macros que serão avaliadas após a compilação combinando eval e backquote. Você vai
e isso matará o contexto para você.
O Swank (para o emacs slime) está cheio desses casos. Eles se parecem com isso:
Não acho que seja um truque imundo. Eu o uso o tempo todo para reintegrar macros em funções.
fonte
Mais alguns pontos em Lisp eval:
fonte
Como a "regra" do GOTO: se você não sabe o que está fazendo, pode fazer uma bagunça.
Além de apenas criar algo com dados conhecidos e seguros, há o problema de que algumas linguagens / implementações não conseguem otimizar o código o suficiente. Você pode acabar com o código interpretado dentro
eval
.fonte
Eval é apenas inseguro. Por exemplo, você tem o seguinte código:
Agora, o usuário chega ao seu site e insere o URL http://example.com/file.php?user= ); $ is_admin = true; echo (
Então o código resultante seria:
fonte
eval
em qualquer idioma que o possua.Eval não é mau. Eval não é complicado. É uma função que compila a lista que você passa para ela. Na maioria dos outros idiomas, compilar código arbitrário significaria aprender o AST do idioma e vasculhar as partes internas do compilador para descobrir a API do compilador. No lisp, você apenas chama eval.
Quando você deve usá-lo? Sempre que você precisar compilar algo, normalmente um programa que aceita, gera ou modifica código arbitrário em tempo de execução .
Quando você não deveria usá-lo? Todos os outros casos.
Por que você não deveria usá-lo quando não precisa? Porque você faria algo de uma maneira desnecessariamente complicada que pode causar problemas de legibilidade, desempenho e depuração.
Sim, mas se eu sou iniciante, como sei se devo usá-lo? Sempre tente implementar o que você precisa com funções. Se isso não funcionar, adicione macros. Se isso ainda não funcionar, avalie!
Siga estas regras e você nunca fará o mal com eval :)
fonte
Gosto muito da resposta de Zak e ele chegou à essência do assunto: eval é usado quando você está escrevendo um novo idioma, um script ou uma modificação de um idioma. Ele realmente não explica mais, então vou dar um exemplo:
Neste programa Lisp simples, o usuário é solicitado a inserir e, em seguida, o que quer que ele insira é avaliado. Para que isso funcione, todo o conjunto de definições de símbolos deve estar presente se o programa for compilado, porque você não tem idéia de quais funções o usuário pode inserir, portanto, você deve incluir todas elas. Isso significa que, se você compilar esse programa simples, o binário resultante será gigantesco.
Por uma questão de princípio, você não pode sequer considerar isso uma declaração compilável por esse motivo. Em geral, depois de usar eval , você está operando em um ambiente interpretado e o código não pode mais ser compilado. Se você não usar eval , poderá compilar um programa Lisp ou Scheme como um programa em C. Portanto, você deseja ter certeza de que deseja e precisa estar em um ambiente interpretado antes de se comprometer a usar o eval .
fonte