왜 평가가 정확히 악한가?
나는 Lisp와 Scheme 프로그래머들이 일반적으로 eval
꼭 필요한 경우가 아니라면 피해야 한다고 말합니다 . 여러 프로그래밍 언어에 대해 동일한 권장 사항을 보았지만의 사용에 대한 명확한 주장 목록을 아직 보지 못했습니다 eval
. 사용의 잠재적 문제에 대한 설명은 어디서 찾을 수 있습니까 eval
?
예를 들어, GOTO
절차 적 프로그래밍 의 문제 (프로그램을 읽을 수없고 유지하기 어렵게 만들고 보안 문제를 찾기 어렵게 만드는 등)를 알고 있지만에 대한 주장을 본 적이 없습니다 eval
.
흥미롭게도, 같은 주장에 GOTO
대해서는 계속해서 반대 해야하지만 Schemers는 예를 들어, 계속이 "악"이라고 말하지 않을 것입니다. 사용시주의해야합니다. eval
연속체를 사용하는 코드보다 코드를 사용하는 것에 비해 눈살을 찌푸 릴 가능성이 훨씬 높습니다 (내가 아는 한 잘못되었을 수 있음).
사용하지 말아야 할 몇 가지 이유가 있습니다 EVAL
.
초보자의 주된 이유는 필요하지 않기 때문입니다.
예 (공통 리스프 가정) :
다른 연산자를 사용하여 표현식을 평가하십시오.
(let ((ops '(+ *)))
(dolist (op ops)
(print (eval (list op 1 2 3)))))
다음과 같이 작성하는 것이 좋습니다.
(let ((ops '(+ *)))
(dolist (op ops)
(print (funcall op 1 2 3))))
Lisp를 배우는 초보자가 필요하다고 생각하는 많은 예제가 EVAL
있지만 표현식이 평가되고 함수 부분도 평가할 수 있기 때문에 필요하지 않습니다. 대부분의 EVAL
경우 평가자에 대한 이해가 부족하다는 것을 나타냅니다.
매크로와 같은 문제입니다. 초보자는 종종 매크로를 작성합니다. 매크로는 실제로 무엇을위한 것인지 이해하지 못하고 함수가 이미 작업을 수행한다는 것을 이해하지 못하는 함수를 작성해야합니다.
작업에 사용하는 도구가 잘못되어 EVAL
있으며 초보자가 일반적인 Lisp 평가 규칙을 이해하지 못하는 경우가 많습니다.
당신이 필요하다고 생각 EVAL
되면 FUNCALL
, REDUCE
또는 비슷한 APPLY
것을 사용할 수 있는지 확인하십시오 .
FUNCALL
-인수가있는 함수를 호출하십시오.(funcall '+ 1 2 3)
REDUCE
-값 목록에서 함수를 호출하고 결과를 결합하십시오.(reduce '+ '(1 2 3))
APPLY
-목록을 인수로 사용하여 함수를 호출하십시오(apply '+ '(1 2 3))
.
Q : eval이 정말로 필요합니까, 아니면 컴파일러 / 평가자가 이미 원하는 것을 이미 가지고 있습니까?
EVAL
약간 더 고급 사용자 를 피해야하는 주요 이유 :
컴파일러가 많은 문제에 대한 코드를 검사하고 더 빠른 코드를 생성 할 수 있기 때문에 코드가 컴파일되었는지 확인하려고합니다. 때로는 MUCH MUCH MUCH (인수 1000 ;-)) 더 빠른 코드
구성되고 평가되어야하는 코드는 가능한 빨리 컴파일 할 수 없습니다.
임의의 사용자 입력 평가로 보안 문제가 발생 함
EVAL
잘못된 시간 에 평가를 사용 하면 빌드 문제가 발생할 수 있습니다.
간단한 예를 통해 마지막 요점을 설명하려면 :
(defmacro foo (a b)
(list (if (eql a 3) 'sin 'cos) b))
그래서 저는 첫 번째 매개 변수의 용도 중 하나를 기반으로하는 매크로를 작성 할 수 있습니다 SIN
또는 COS
.
(foo 3 4)
수행 (sin 4)
및 (foo 1 4)
수행합니다 (cos 4)
.
이제 우리는 :
(foo (+ 2 1) 4)
원하는 결과를 얻지 못합니다.
그런 다음 FOO
변수를 평가하여 매크로를 복구하려고 할 수 있습니다 .
(defmacro foo (a b)
(list (if (eql (eval a) 3) 'sin 'cos) b))
(foo (+ 2 1) 4)
그러나 이것은 여전히 작동하지 않습니다.
(defun bar (a b)
(foo a b))
변수 값은 컴파일 타임에 알려지지 않았습니다.
피해야 할 일반적인 중요한 이유 EVAL
: 추악한 해킹에 자주 사용됩니다.
eval
(어떤 언어로든) 전기 톱이 악한 것과 같은 방식으로 악한 것은 아닙니다. 도구입니다. 잘못 사용하면 팔다리를 절단하고 (비 유적으로 말하면) 볼 수있는 강력한 도구이지만, 프로그래머의 도구 상자에있는 많은 도구에 대해서도 다음과 같이 말할 수 있습니다.
goto
그리고 친구들- 잠금 기반 스레딩
- 연속
- macros (hygenic or other)
- pointers
- restartable exceptions
- self-modifying code
- ...and a cast of thousands.
If you find yourself having to use any of these powerful, potentially dangerous tools ask yourself three times "why?" in a chain. For example:
"Why do I have to use
eval
?" "Because of foo." "Why is foo necessary?" "Because ..."
If you get to the end of that chain and the tool still looks like it's the right thing to do, then do it. Document the Hell out of it. Test the Hell out of it. Double-check correctness and security over and over and over again. But do it.
Eval is fine, as long as you know EXACTLY what is going into it. Any user input going into it MUST be checked and validated and everything. If you don't know how to be 100% sure, then don't do it.
Basically, a user can type in any code for the language in question, and it will execute. You can imagine for yourself how much damage he can do.
"When should I use eval
?" might be a better question.
The short answer is "when your program is intended to write another program at runtime, and then run it". Genetic programming is an example of a situation where it likely makes sense to use eval
.
IMO, this question is not specific to LISP. Here is an answer on the same question for PHP, and it applies to LISP, Ruby, and other other language that has an eval:
The main problems with eval() are:
- Potential unsafe input. Passing an untrusted parameter is a way to fail. It is often not a trivial task to make sure that a parameter (or part of it) is fully trusted.
- Trickyness. Using eval() makes code clever, therefore more difficult to follow. To quote Brian Kernighan "Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it"
The main problem with actual use of eval() is only one:
- inexperienced developers who use it without enough consideration.
Taken from here.
I think the trickyness piece is an amazing point. The obsession with code golf and concise code has always resulted in "clever" code (for which evals are a great tool). But you should write your code for readability, IMO, not to demonstrate that you're a smarty and not to save paper (you won't be printing it anyway).
Then in LISP there's some problem related to the context in which eval is run, so untrusted code could get access to more things; this problem seems to be common anyway.
There have been many great answers but here is another take from Matthew Flatt, one of the implementers of Racket:
http://blog.racket-lang.org/2011/10/on-eval-in-dynamic-languages-generally.html
He makes many of the points that have already been covered but some people may find his take interesting nonetheless.
Summary: The context in which it's used affects the result of eval but is often not considered by programmers, leading to unexpected results.
The canonical answer is to stay away. Which I find weird, because it's a primitive, and of the seven primitives (the others being cons, car, cdr, if, eq and quote), it gets far and away the least amount of use and love.
From On Lisp: "Usually, calling eval explicitly is like buying something in an airport gift-shop. Having waited till the last moment, you have to pay high prices for a limited selection of second-rate goods."
So when do I use eval? One normal use is to have an REPL within your REPL by evaluating (loop (print (eval (read))))
. Everyone is fine with that use.
But you can also define functions in terms of macros that will be evaluated after compilation by combining eval with backquote. You go
(eval `(macro ,arg0 ,arg1 ,arg2))))
and it will kill the context for you.
Swank (for emacs slime) is full of these cases. They look like this:
(defun toggle-trace-aux (fspec &rest args)
(cond ((member fspec (eval '(trace)) :test #'equal)
(eval `(untrace ,fspec))
(format nil "~S is now untraced." fspec))
(t
(eval `(trace ,@(if args `(:encapsulate nil) (list)) ,fspec ,@args))
(format nil "~S is now traced." fspec))))
I don't think it's a filthy hack. I use it all the time myself to reintegrate macros into functions.
Another couple of points on Lisp eval :
- It evaluates under the global environment, losing your local context.
- Sometimes you may be tempted to use eval, when you really meant to use the read-macro '#.' which evaluates at read-time.
Like the GOTO "rule": If you don't know what you are doing, you can make a mess.
Besides from only building something out of known and safe data, there's the problem that some languages/implementations can't optimize the code enough. You could end up with interpreted code inside eval
.
Eval is just unsecure. For example you have following code:
eval('
hello('.$_GET['user'].');
');
Now user comes to your site and enters url http://example.com/file.php?user=);$is_admin=true;echo(
Then the resulting code would be:
hello();$is_admin=true;echo();
Eval is not evil. Eval is not complicated. It is a function that compiles the list you pass to it. In most other languages, compiling arbitrary code would mean learning the language's AST and digging around in the compiler internals to figure out the compiler API. In lisp, you just call eval.
When should you use it? Whenever you need to compile something, typically a program that accepts, generates or modifies arbitrary code at runtime.
When shouldn't you use it? All other cases.
Why shouldn't you use it when you don't need to? Because you would be doing something in an unnecessarily complicated way that may cause problems for readability, performance and debugging.
Yeah, but if I'm a beginner how do I know if I should use it? Always try to implement what you need with functions. If that doesn't work, add macros. If that still doesn't work, then eval!
Follow these rules and you'll never do evil with eval :)
I like Zak's answer very much and he has gotten at the essence of the matter: eval is used when you are writing a new language, a script, or modification of a language. He doesn't really explain further so I will give an example:
(eval (read-line))
In this simple Lisp program, the user is prompted for input and then whatever they enter is evaluated. For this to work the entire set of symbol definitions has to be present if the program is compiled, because you have no idea which functions the user might enter, so you have to include them all. That means that if you compile this simple program, the resulting binary will be gigantic.
As a matter of principle, you can't even consider this a compilable statement for this reason. In general, once you use eval, you are operating in an interpreted environment, and the code can no longer be compiled. If you do not use eval then you can compile a Lisp or Scheme program just like a C program. Therefore, you want to make sure you want and need to be in an interpreted environment before committing to using eval.
참고URL : https://stackoverflow.com/questions/2571401/why-exactly-is-eval-evil
'Programing' 카테고리의 다른 글
PHP에서 Null vs. False vs. 0 (0) | 2020.06.20 |
---|---|
HTML 사이트에 사용자 정의 글꼴을 설치하는 방법 (0) | 2020.06.20 |
Ruby 콘솔에서 고안 사용자 생성 (0) | 2020.06.20 |
Numpy-배열에 행 추가 (0) | 2020.06.20 |
jQuery에서 요소의 n 번째 수준 부모를 얻으려면 어떻게해야합니까? (0) | 2020.06.20 |