问题
I am writing a program in Processing that transforms complex numbers. However, I want to have a method of taking an input string and calculating the transformation using a complex variable. For example:
1/(z+1)
(z^2)/(z/2)
where z is a complex number. Now, I've looked at JEP and some examples, but I cannot work out if it would allow you to actually enter z as a variable (and in any case it is not free). Is there an expression parser for Java (that works in processing, which uses an old version of java and does not have generics) that I could use to do this?
If there is not, could someone point me to the basics of how to create one?
回答1:
As mentioned by PhiLo, you can use generics. Try this Processing sketch:
import java.util.*;
java.util.List<String> list = Arrays.asList("a", "b", "c");
textFont(loadFont("UMingCN-30.vlw"));
for(int i = 0; i < list.size(); i++) {
text(list.get(i), 5, int(i*30)+30);
}
And there's a non commercial version of JEP available (GPL). Download it here and add it to your Processing classpath (import it). After successfully doing so, you can use JEP like this:
void setup() {
org.nfunk.jep.JEP parser = new org.nfunk.jep.JEP();
parser.addComplex();
try {
parser.parseExpression("(1+2*i) + (3+8*i)");
println(parser.getComplexValue());
} catch(Exception e) {
e.printStackTrace();
}
}
which produces the (expected) output: (4.0, 10.0)
回答2:
Have a look at this: http://bracer.sourceforge.net It's my implementation of shunting-yard algorithm and this parser supports complex numbers.
回答3:
If for some reason you need more flexibility than the "canned" complex math expression parsers suggested so far (= full control over operators, precedence, tree construction), you may want to consider my configurable parser:
https://github.com/stefanhaustein/expressionparser
Example direct evaluation code for your case:
static HashMap<String, Complex> variables = new HashMap<>();
/**
* Processes the calls from the parser directly to a Complex value.
*/
static class ComplexProcessor extends ExpressionParser.Processor<Complex> {
@Override
public Complex infixOperator(ExpressionParser.Tokenizer tokenizer, String name, Complex left, Complex right) {
switch (name.charAt(0)) {
case '+': return left.plus(right);
case '-': return left.minus(right);
case '*': return left.times(right);
case '/': return left.divides(right);
case '^':
if (right.im() != 0 || right.re() == (int) right.re()) {
return left.pow((int) right.re());
}
throw new RuntimeException("Only integer exponents supported by Complex.pow().");
default:
throw new IllegalArgumentException();
}
}
@Override
public Complex prefixOperator(ExpressionParser.Tokenizer tokenizer, String name, Complex argument) {
return name.equals("-") ? new Complex(0,0).minus(argument) : argument;
}
@Override
public Complex numberLiteral(ExpressionParser.Tokenizer tokenizer, String value) {
return new Complex(Double.parseDouble(value), 0);
}
@Override
public Complex identifier(ExpressionParser.Tokenizer tokenizer, String name) {
Complex value = variables.get(name);
if (value == null) {
throw new IllegalArgumentException("Undeclared variable: " + name);
}
return value;
}
@Override
public Complex group(ExpressionParser.Tokenizer tokenizer, String paren, List<Complex> elements) {
return elements.get(0);
}
/**
* Creates a parser for this processor with matching operations and precedences set up.
*/
static ExpressionParser<Complex> createParser() {
ExpressionParser<Complex> parser = new ExpressionParser<Complex>(new ComplexProcessor());
parser.addCallBrackets("(", ",", ")");
parser.addGroupBrackets("(", null, ")");
parser.addOperators(ExpressionParser.OperatorType.INFIX_RTL, 4, "^");
parser.addOperators(ExpressionParser.OperatorType.PREFIX, 3, "+", "-");
// 2 Reserved for implicit multiplication
parser.addOperators(ExpressionParser.OperatorType.INFIX, 1, "*", "/");
parser.addOperators(ExpressionParser.OperatorType.INFIX, 0, "+", "-");
return parser;
}
}
Example invocation:
variables.put("i", new Complex(0, 1));
variables.put("z", new Complex(1, 1));
ExpressionParser<Complex> parser = ComplexProcessor.createParser();
System.out.println("(z^2)/(z/2):", parser.parse("(z^2)/(z/2)"));
The parser itself is implemented in a single java file without dependencies, so for evaluation purposes it's simple to copy to your own project
回答4:
I would (and have, actually) manually make a parse table and use a simple LR or LALR parser to process it. At a reduction, you can perform the calculations. One advantage to this is that it is easy to modify the "language", or acceptable input.
回答5:
Here's crazy solution: java has built-in JavaScript engine (I suppose you can access it from Processing). Now, you write a javascript class that works with complex numbers(copy it from here). Then, overload math operators as specified here. AFter that you can just eval this string from java. It's crazy and I'm not sure that it will work (i don't know javascript). Maybe it will make to find some simplier solution without parsing expressions.
回答6:
Here is a link to a straight-forward math expression parser (64 lines): http://javadots.blogspot.com/2008/11/arithemetic-expressions-solver-in-64.html
Tweaking it to support your needs should not be too difficult
回答7:
Use Apache Common Math. It is very easy to use.
You can initialize both real+imaginary parts. You can also initialize them from a string. It supports a wide array of operations that you can do with imaginary numbers.
Here is a example of code for doing some common operations:
package complex;
import static java.lang.String.format;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.math3.complex.Complex;
import org.apache.commons.math3.complex.ComplexFormat;
public class Do
{
public static void main(String[] args)
{
ComplexFormat format = new ComplexFormat();
Complex lhs = new Complex(1.0, 3.0);
Complex rhs = new Complex(2.0, 5.0);
Complex answer = lhs.add(rhs); // add two complex numbers
System.out.println("Add : "+ format.format(answer));
answer = lhs.subtract(rhs); // subtract two complex numbers
System.out.println("Subtract : "+ format.format(answer));
answer = lhs.conjugate();
System.out.println("Conjgate : "+ format.format(answer));
double d = lhs.abs();
System.out.println("Absolute : "+d);
Complex first = new Complex(1.0, 3.0);
Complex second = new Complex(2.0, 5.0);
answer = first.log(); // natural logarithm.
System.out.println("Logarithm : "+ format.format(answer));
answer = first.cos(); // cosine
System.out.println("Cosine : "+ format.format(answer));
answer = first.pow(second); // first raised to the power of second
System.out.println("Power : "+ format.format(answer));
Complex z = new Complex(2.0,2.0);
Complex z1 = z.reciprocal();
System.out.println("Recipocal : "+ format.format(z1));
System.out.println("Absoltue of 2+2i is "+z.abs());
System.out.println("Argument of 2+2i is "+z.getArgument());
Complex r = new Complex(6.3,9.6);
String conj = format.format(r.conjugate());
String reci = format.format(r.reciprocal());
System.out.println("Conjugate : "+conj+" Recipocal : "+reci);
//answer = lhs.abs(); // absolute value
//answer = lhs.conjugate(rhs); // complex conjugate
//make complex to string
ComplexFormat format = new ComplexFormat(); // default format
Complex c = new Complex(1.1111, 2.2222);
String s = format.format(c); // s contains "1.11 + 2.22i"
System.out.println(s);
//make string to complex
String z = "2.5+3.6i";
Complex e = format.parse(z);
System.out.println(e);
}
}
Another alternative is FrAid, if you want another option.
来源:https://stackoverflow.com/questions/1446466/java-math-expression-parser-that-can-take-complex-numbers-as-a-variable