방법 중 하나를 간주한다. 그런 다음 함수 객체는 AST를 저장하고 호출 될 때 파싱합니다. 일반적으로 더 간단합니다. 물론
당신이 잘 이해할 수 있지만, 구문 분석하는 것은 쉽지 않다 : 귀하의 예를 고려, f(a, b) = 2 * a * b
이 비슷한 구체적인 구문 트리에 구문 분석 할 것입니다!2 * a * b
이라는 표현식은 문자열과 거의 비슷하게 쓰여지므로 트리를보고 연산자 우선 순위를 알지 못합니다 (2 + a * b
은 2 + (a * b)
또는 (2 + a) * b
을 의미합니까?) 올바른 순서로 트래버스하는 데 약간의 시간이 걸립니다.
그러나, 우리는 이해하기 위해 기계 쉽게 더 신뢰할 수있는 무언가를 구축 한 번만 구문 분석 할 수 있습니다 :
네 아, 이제 구문 분석하는 정말 쉽습니다 : 그 params.length
매개 변수와 함께 호출 HashMap 또는 스코프를 나타내는 모든 것을 설정하면, 파라미터 2
및 표현식 *(a,b)
을 가진 "함수"*
을 호출합니다. 따라서 a
과 b
을 "function"*
에 전달하여 호출합니다. 물론 이것은 사용자 정의 함수로 쉽게 확장 가능합니다. 대서양 표준시가 좋아, 쉽게 구문 분석
: 함수 abs (value) = max(value, -value)
을 고려
는
, 우리는 유사한 AST를 구축 할 것입니다. 그러나 그것을 구축하는 것은 어떨까요? 모든 연산자를 함수로 간주하는 경우 (대부분 (num, num) -> num
유형, (num) -> num
유형 (단항) 유형) 이 트리의 노드에 대해 매우 안정적인 정의가 있습니다.
interface AstNode {
double eval(Scope scope); // you can look at scope as a HashMap<String, double>
}
class TerminalNode : AstNode {
String varName;
double constantValue;
public TerminalNode(String varName) {
this.varName = varName;
}
public TerminalNode(double constantValue) {
this.constantValue = constantValue;
this.varName = null;
}
public double eval(Scope scope) {
if (varName == null) return constantValue;
return scope.get(varName);
}
}
class CallNode : AstNode {
Function target;
String[] parameters;
AstNode[] children;
public CallNode(Function target, String[] parameters, AstNode[] children) {
this.target = target;
this.parameters = parameters;
this.children = children;
}
public double eval(Scope scope) {
double[] subExpressions = new double[children.length];
Scope innerScope = new Scope(scope); // creates a copy
for (int i = 0; i < children.length; i++) {
// I'm using the copy here because of the edge-case: f(x) = g(x) + x; g(x) = x * 2;
// In this case, the x variable is overriden in the innerScope when we resolve g(x)
// but we "stored" the previous x value in the "outerScope" so we can add it later
subExpressions[i] = children[i].eval(scope);
innerScope.set(target.getParamName(i), subExpressions[i]);
}
// usually, you could do target.getNode().eval(innerScope)
// however, this would limit the target function to only be user-defined functions
// we leave this way so you can override the Function's eval method to a built-in
return target.eval(innerScope);
}
}
이것은 단순화 될 수 있지만 좋은 출발점입니다. 알다시피, CallNode
은 다른 자식이 AstNode
이므로, 모든 자식이 TerminalNode
(변수 또는 상수) 일 때 무한 재귀입니다. 코드에 주석으로 설명 된대로 클래스의 인스턴스화 또는 확장을 통해 Function
클래스의 멤버로 연산자를 작성할 수 있습니다. 물론 이것은 Function
구현에 따라 다릅니다. 또 다른 방법은 다른 AstNode
클래스, 즉 BuiltinNode
(또는 심지어 모든 노드 PlusNode
, DivideNode
등)을 빌드하여 프리미티브를 사용하여 호출을 해결하는 것입니다.
"빌드 된 AST를 사용하는 방법"에 대한 답변에이 내용을 추가하십시오. g
이라는 Function
개체가 있는데이 개체는 식 f(x, y) = 2 * a * b
에 대한 AST를 저장한다고 가정합니다. f(4, 2)
값을 얻는 방법은 무엇입니까?
이렇게하려면 Scope
개체 (또는 중요하게는 HashMap<String, double>
)를 사용합니다. 매개 변수가 결정된 함수의 범위를 만든 다음 내부 수준에이 범위를 사용하는 AST를 사용하여 함수를 호출합니다. 같은
코드가 보일 수 있습니다
Scope scope = new Scope();
scope.set("x", 4);
scope.set("y", 2);
// remember I stored the function f on the object g, just to make a distinction
// also, note this is equivalent to g.getNode().eval(scope), because the function stored in g is not a built-in!
System.out.println(g.eval(scope));
이 eval
쿼리를 해결하기 위해 AST (우리는 그것을 만든) 미리 범위 {x: 4, y: 2}
이되며, a=2
및 b=x*y
와 함수 *(a, b)
를 호출합니다. 첫 번째 *
함수 호출을 해결하려면 두 인수 (a
및 b
)를 모두 해결해야합니다. 해결하려면 a
은 터미널 노드이기 때문에 쉽습니다 (eval
은 터미널을 즉시 반환합니다).b
을 풀려면 내부 함수의 평가를 호출하여 {x: 4, y: 2, a: x, b: y}
이라는 새 범위를 생성해야합니다. 여기서 a
과 b
은 두 번째 *
함수의 인수입니다. a
과 b
둘 다 터미널 노드로 해결 된 다음 *
에 대한 두 번째 호출은 (4*2
= 8
을 계산하는 기본 제공 함수로) 그 값을 계산하여 호출자에게 반환 할 수 있습니다.이 값은 첫 번째 *
인 경우 b
입니다 기능.
지금
x
및
y
는
f
및
a
및
b
의 파라미터는
*
함수의 인자되어있는
{x: 4, y: 2, a: 2, b: 8}
같이 범위를 갖는. 모든 인수를 설정하면
*
내장 함수를 호출하여 함수의 결과 인
16
을 얻을 수 있습니다!
이미지는 많이, http://ironcreek.net/phpsyntaxtree
와우에 의해 감사를 정말 자세히 답을 생성. 그러나 나는 Antlr을 가진 단단한 noob입니다. 그래서 Antlr이 tress를 생성하는 문법 규칙을 찾았습니다. http://i.stack.imgur.com/r0W8w.png 그런 다음 f (2 , 4). 임 확실하지 않아, 그때 내가 구체적으로해야 할 일. 이것은 내 주요 문제와 같다. 나는 변수 a와 b에 대해 매개 변수 2와 4를 사용하도록 프로그램에 "알리는"방법을 모른다. – FelRPI
@FelRPI 사용법 설명과 함께 새로운 섹션이 추가되었습니다. – Mephy
감사합니다. 매우 인상 깊었습니다. 내 스스로 AST를 만들 필요가 있는지, 아니면 antlr4 문법 인 경우 http://i.stack.imgur.com/G6Q7Y.png처럼 보이는 AST를 작성하는지 궁금합니다. soo는 건너 뛸 수 있습니다. 혼자서 만들어야합니다. 아마 그냥 혼란스러워 할 것입니다. 이 프로젝트에 너무 많은 시간 동안 일했습니다. PUB – FelRPI