Prof. Harley Flanders wrote:
"Except that you have to code every single case separately, 2x6, 3x10, 4x4x4, etc. You need to write a separate routine for every shape of array you want to use."
Not so:
Use open arrays (always 0-based):
type Matrix = array of array of TRing;
[...]
Just as a reminder, this is the GPC list. Discussion of compiler features for comparison is welcome, but posting of (I suppose) FPC specific code would fit better on the FPC list, I think.
The problem I do not yet know how to solve: to do it for all TRing.
So I need a type TRing that can include any structure that has + and * operators, and a 0 element:
Integers, Floats, Rationals, Gaussian Integers, Polynomials with xxx coefficients,... any possible data type I can define with overloaded +, *, etc.
That's more or less exactly the description of (one of) the problems that templates are there to solve.
Note that 0 is a problem in itself -- one really needs to overload it too!
Indeed, I intentionally omitted this discussion in my previous mail to avoid distraction.
Some ideas:
- Define a constructor for each relevant type that initializes to the zero element. Problem: It would fail for simple types (integer, real) which are not initialized by default and cannot have user-defined constructors.
- Define an (overloaded) procedure "Zero" (or so). A function would seem more natural, but since it would take no parameters, it couldn't be overloaded. (C++ would allow it as a template function, but to disambiguate, each call would need to specify the type, as in "Zero <MyRing> ()".)
- Since for each ring multiplication with an integer is well-defined, you could require an operator * (n: Integer; x: MyRing): MyRing; and to get Zero, you can do "0 * x" with any ring element x (such as the first element of one of the operand matrices). Though this might produce slightly non-optimal code, but it's a more general solution.
- Of course, you can write "x - x" (using an arbitrary element) whenever you need a 0, but that's also likely to produce non-topimal code in many cases.
- If you want to assume a ring with 1, you can even define conversion from integer to the ring (as n -> n * One). In C++, you can do this by implementing additional constructors (i.e., the take an integer parameter n, and construct n * One). Though you can't define constructors for built-in types, this doesn't hurt in this case, since they already support this conversion (trivial for integer types, automatic integer -> real conversion).
- If you don't like the last one, you could define a singleton "Zero" element (i.e., an element of a new type ("TZero") that isn't used anywhere else and doesn't carry meaningful data), and define a constructor that takes a "TZero" parameter and constructs the zero element. (It wouldn't actually use the value of the "TZero" element, just its static (compile-time) type information, so it could be optimally efficient when inlined.) Again, built-in types can have no user-defined constructors, but at least in C++, this could be solved by defining "type-cast operators" of the "TZero" type to integer, real, etc.
Frank