K
Kaz Kylheku
Svein Ove Aas said:At this point I'd like to point out that, although complex numbers are part
of the standad CL library, if they weren't, it would be fairly easy to add
them.
Also, I'd like to point out that some things you can do with Lisp
complex numbers are difficult in C++.
Firstly, if you use CLOS methods for arithmetic operations, you can
support all of the combinations. C++ can only do this statically,
because dynamic dispatch (virtual functions) is done on what is
effectively the leftmost argument of a method (i.e. the object, or
``this'' pointer).
In Lisp, some block of code can receive two numbers whose type is not
statically apparent, and call an operation. The right operation is
selected based on both their types. This is called multiple dispatch:
the method selection takes into account the type of all objects---the
run-time type---somewhat analogously to how C++ overload resolution
does it over static types.
There are hacks to do this in C++ such as the Visitor Pattern.
Dynamically dispatch on the left argument to get to implementation i
of virtual function X, which then dynamically dispatches an
implementation of Y_i on the right argument. If you want three
arguments, the complexity explodes further; you then want to write a
Perl script to just generate the tedious C++ from a condensed spec.
Secondly, the Lisp numeric type system incorporates the idea that
value influences type. For instance, a complex number whose imaginary
part is zero is a real number. Or a rational number whose denominator
is one has integer type. The return value of an arithmetic computation
does not have some fixed declared type; the type is determined by the
value that is produced. If you divide 2 and 3, you get the rational
2/3. If you divide 4 by 2 using exactly the same function, you get
integer 2. Take the square root of -1 with (sqrt -1) and you get the
complex number #c(0 1). And so on.
Lastly, the two components of a complex number are not necessarily
real numbers. They can be fixed integers or bignums. This is largely
transparent.
For example, suppose you add these two complex numbers like this:
(+ #c(-1 -1) #c(1 1))
The result is 0. That is, to say, *integer* zero. Not a complex zero,
or real zero. The real and imaginary parts of these complexes are
integers, and they all nicely cancel out to produce a pure zero.
These types of robust properties of the Lisp numeric tower make it
useful for solving numeric problems, with clear, concise code in which
The Right Thing happens.