Thread overview | |||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
November 26, 2002 Templates and matrices | ||||
---|---|---|---|---|
| ||||
Hi, When reading the D documentation for arrays, we see that every dynamic array is ragged and every static array is rectangular. For a efficient matrix library we should be able to provide either: template Matrix(T) { T[][] reversed(T[][] matrix) { ... } } Matrix(int) matrix int[5][5] aMatrix = ...; matrix.reversed(aMatrix); and expect automatic conversion when using rectangular arrays, or template Matrix(T) { T reversed(T matrix) { ... } } Matrix(int[5][5]) matrix int[5][5] aMatrix = ...; matrix.reversed(aMatrix); And always instantiate a more specific template instance for every array dimension used. This last solution is similar to C++ usage of templates with int parameters. If this possibility was available in D we could write: template Matrix(T, int rows, int columns) { T[rows][columns] reversed(T[rows][columns] matrix) { ... } } Matrix(int, 5, 5) matrix int[5][5] aMatrix = ...; matrix.reversed(aMatrix); And use it anyway we want. The first solution is cleaner and more intuitive, but the compiler should be able to correctly create additional function definitions for different array dimensions (which are different types). The last solution opens the door to non-type parameters in templates, which are very good to write generic modules: template TBinaryTree(T, int fun(T, T) comparator) { ... } With this approach we have templates capable of generic programming like in Ada, ML or Haskell. But currently is not possible to use the efficiency of D's rectangular arrays and the genericity of D's templates without some tricks. Just some thoughts... Best regards, Daniel Yokomiso. "I will bend your mind with my spoon." |
November 26, 2002 Re: Templates and matrices | ||||
---|---|---|---|---|
| ||||
Posted in reply to Daniel Yokomiso | You have some good ideas. I've been looking at some improvements to the array conversions between dynamic & static arrays, but it'll be a bit yet before such is implemented. -Walter "Daniel Yokomiso" <daniel_yokomiso@yahoo.com.br> wrote in message news:arumno$1i2g$1@digitaldaemon.com... > Hi, > > When reading the D documentation for arrays, we see that every dynamic > array is ragged and every static array is rectangular. For a efficient > matrix library we should be able to provide either: > > template Matrix(T) { > T[][] reversed(T[][] matrix) { ... } > } > Matrix(int) matrix > int[5][5] aMatrix = ...; > matrix.reversed(aMatrix); > > > and expect automatic conversion when using rectangular arrays, or > > template Matrix(T) { > T reversed(T matrix) { ... } > } > Matrix(int[5][5]) matrix > int[5][5] aMatrix = ...; > matrix.reversed(aMatrix); > > > And always instantiate a more specific template instance for every array > dimension used. This last solution is similar to C++ usage of templates with > int parameters. If this possibility was available in D we could write: > > template Matrix(T, int rows, int columns) { > T[rows][columns] reversed(T[rows][columns] matrix) { ... } > } > Matrix(int, 5, 5) matrix > int[5][5] aMatrix = ...; > matrix.reversed(aMatrix); > > And use it anyway we want. The first solution is cleaner and more > intuitive, but the compiler should be able to correctly create additional > function definitions for different array dimensions (which are different > types). The last solution opens the door to non-type parameters in > templates, which are very good to write generic modules: > > template TBinaryTree(T, int fun(T, T) comparator) { ... } > > > With this approach we have templates capable of generic programming like > in Ada, ML or Haskell. But currently is not possible to use the efficiency > of D's rectangular arrays and the genericity of D's templates without some > tricks. > Just some thoughts... > > Best regards, > Daniel Yokomiso. > > "I will bend your mind with my spoon." > > |
November 27, 2002 Re: Templates and matrices | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | "Walter" <walter@digitalmars.com> escreveu na mensagem news:as1108$15fo$3@digitaldaemon.com... > You have some good ideas. I've been looking at some improvements to the array conversions between dynamic & static arrays, but it'll be a bit yet before such is implemented. -Walter Hi, Ok, I'll implement a dumb Matrix class (using hand written packing of data to mimic rectangular behaviour). Speaking about matrices, why the following code fails to link? int main() { const int[][] matrix = [[0,1,2,3], [4,5,6,7], [8,9,10,11], [12,13,14,15]]; const int[][] sub0 = [[0,1,2,3]]; const int[][] sub1 = [[0,1,2,3], [4,5,6,7]]; const int[][] sub2 = [[4,5,6,7], [8,9,10,11]]; const int[][] sub3 = [[5,6], [9,10]]; assert(matrix[0] == sub0[0]); assert(matrix == matrix); assert(matrix[0..1] == sub0); assert(matrix[0..2] == sub1); assert(matrix[1..3] == sub2); assert(matrix[1..3][1..3] == sub3); return 0; } It gives a message 'Error 42: Symbol Undefined __init_TypeInfo_Ai' And are multidimensional slices valid? Id would be great to matrix programming, so we could write: A[1..3][4..10] += B[5..7][8..14]; Or something like that. Also overloading of the [] operator for subscript and slicing would be a good idea, together with the in operator. And the last thing, I'm stuck with a problem of them current template syntax. Right now I have a template Matrix class defined for any kind of numeric type. But for some operations like the trace of the matrix (sum of the main diagonal elements) I need to have a zero value like: T trace() { T sum = 0; // code to sum the main diagonal elements return sum; } For TMatrix(int) or TMatrix(float) instances this code is ok, but when I start using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now I can solve this by using a template variable to hold the value of zero and code verifications to ensure that the zero value is defined before the programmer uses a Matrix. instance TMatrix(Quatertion) tmatrix; tmatrix.zero = new Quatertion(); tmatrix.Matrix m = new tmatrix.Matrix(10, 10); where new Quatertion() is the zero value for quaternions. Haskell, ML and Ada allow any kind of parameters, besides types, when you instantiate a generic module. I think should follow its example, so our could would be something like: instance TMatrix(Quatertion, new Quatertion()) tmatrix; tmatrix.Matrix m = new tmatrix.Matrix(10, 10); I keep thinking of it as a template constructor. Maybe we could push this template parametrization stuff to module level, and the programmer would use a template module as a regular module: module matrix(T, T zero); // declaration of template module import matrix(int, 0); import matrix(float, 0.0); import matrix(Quaternion, new Quaternion()); And use the module values without any particular need to refer to the instance variable. I think it's still time for a change like that, AFAIK I'm the only one doing serious template work with D, and I can live with any change in the language. It would make the language simpler for template users (if we make matrix programming right we can get lots of people interested in doing scientific programming in D, it's my current goal). Best regards, Daniel Yokomiso. "The best model of the solar system is not an orange for the sun and various fruits at appropriate distances. Even if the orange is a good approximation to the sun in form and color. But the best model is a set of differential equations. Last time I looked they were neither round nor orange, but they are definitely a better model." - Joachim Durchholz |
November 27, 2002 Re: Templates and matrices | ||||
---|---|---|---|---|
| ||||
Posted in reply to Daniel Yokomiso | "Daniel Yokomiso" <daniel_yokomiso@yahoo.com.br> wrote in message news:as1944$1e3u$2@digitaldaemon.com... > Speaking about matrices, why the following code fails to link? > It gives a message 'Error 42: Symbol Undefined __init_TypeInfo_Ai' Because I have failed to write a typeinfo for arrays of ints :-( See the ti*.d files in \dmd\src\phobos. > And are multidimensional slices valid? Id would be great to matrix > programming, so we could write: > A[1..3][4..10] += B[5..7][8..14]; > Or something like that. I'd have to think about that. I don't think it will work for rectangular arrays. > Also overloading of the [] operator for > subscript and slicing would be a good idea, together with the in operator. Yes, that's a good idea. > And the last thing, I'm stuck with a problem of them current template > syntax. Right now I have a template Matrix class defined for any kind of > numeric type. But for some operations like the trace of the matrix (sum of > the main diagonal elements) I need to have a zero value like: > T trace() { > T sum = 0; > // code to sum the main diagonal elements > return sum; > } > For TMatrix(int) or TMatrix(float) instances this code is ok, but when I > start using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now I can solve this by using a template variable to hold the value of zero and code verifications to ensure that the zero value is defined before the programmer > uses a Matrix. I don't understand. > instance TMatrix(Quatertion) tmatrix; > tmatrix.zero = new Quatertion(); > tmatrix.Matrix m = new tmatrix.Matrix(10, 10); > > where new Quatertion() is the zero value for quaternions. Haskell, ML and Ada allow any kind of parameters, besides types, when you instantiate a generic module. I think should follow its example, so our could would be something like: > > instance TMatrix(Quatertion, new Quatertion()) tmatrix; > tmatrix.Matrix m = new tmatrix.Matrix(10, 10); That's probably a good idea. I wanted to try it with just types for starters and see how far that got. > I keep thinking of it as a template constructor. Maybe we could push > this template parametrization stuff to module level, and the programmer > would use a template module as a regular module: > > > module matrix(T, T zero); // declaration of template module > > import matrix(int, 0); > import matrix(float, 0.0); > import matrix(Quaternion, new Quaternion()); > > > And use the module values without any particular need to refer to the > instance variable. I think it's still time for a change like that, AFAIK I'm > the only one doing serious template work with D, and I can live with any change in the language. It would make the language simpler for template users (if we make matrix programming right we can get lots of people interested in doing scientific programming in D, it's my current goal). Ah, parameterized modules! Interesting shorthand! |
November 27, 2002 Re: Templates and matrices | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | Walter wrote: > "Daniel Yokomiso" <daniel_yokomiso@yahoo.com.br> wrote in message > news:as1944$1e3u$2@digitaldaemon.com... > >> Speaking about matrices, why the following code fails to link? >> It gives a message 'Error 42: Symbol Undefined __init_TypeInfo_Ai' > > Because I have failed to write a typeinfo for arrays of ints :-( See the > ti*.d files in \dmd\src\phobos. Hooray! The more you have to special-case the easier a proper implementation will be in comparison. ;-) >> And are multidimensional slices valid? Id would be great to matrix >>programming, so we could write: >>A[1..3][4..10] += B[5..7][8..14]; >> Or something like that. > > I'd have to think about that. I don't think it will work for rectangular > arrays. I'll make the problem explicit. Given: int [10] [10] a; The resulting type from "a [5] [4]" is int, but the resulting type from "a [5 .. 10] [4 .. 5]" is int[][]. Indices remove a layer, but slicing doesn't, so a double-slice should still result in the same number of dimensions. Here's where they got in the discussion, Daniel. Pavel suggested the syntax: int [r, c] a; So if that's a matrix, I could get a column or a row by "a [0 .. r, c]" or "a [r, 0 .. c]", or get sub-matrices out of that. Powerful stuff, but hard on the compiler and really hard on operator overloading. Needs solid justification. >>Also overloading of the [] operator for >>subscript and slicing would be a good idea, together with the in operator. > > Yes, that's a good idea. For comparison, Python's method names are (with the bracketing "__" removed): T getitem (U key); T setitem (U key, T value); void delitem (U key); X iter (); /* Return an iterator. */ bit contains (U key); /* For the "a in b" relationship. */ V getslice (U i, U j); V setslice (U i, U j, V sequence); void delslice (U i, U j); Slices were made a part of the language in 2.0; I don't think their reasons for making the switch are applicable to us. >> And the last thing, I'm stuck with a problem of them current template >>syntax. Right now I have a template Matrix class defined for any kind of >>numeric type. But for some operations like the trace of the matrix (sum of >>the main diagonal elements) I need to have a zero value like: >>T trace() { >> T sum = 0; >> // code to sum the main diagonal elements >> return sum; >>} >> For TMatrix(int) or TMatrix(float) instances this code is ok, but when > > I > >>start using it for user-defined classes that are not addable to 0, like >>Vector, Quaternion, Equation, etc. the code stops working. Right now I can >>solve this by using a template variable to hold the value of zero and code >>verifications to ensure that the zero value is defined before the > > programmer > >>uses a Matrix. > > > I don't understand. Take the code: T sum (T [] list) { T result = 0; for (T *c = list, e = c + list.length; c < e; c ++) result += *c; return result; } This is fine if T is int or float, but if it's "struct vec3 { float x, y, z; }" then the initial value of result doesn't make sense. Howsabout the builtin types have an attribute ".zero" that we can put into our user types as static variables/constants. Then that line would be: T result = T.zero; Which I think would work better than C++'s constructor method. zero could even be a static property, and if it's proveably const then the compiler can do lots with it. |
November 27, 2002 Re: Templates and matrices | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | Reply embedded "Walter" <walter@digitalmars.com> escreveu na mensagem news:as24jh$2b30$1@digitaldaemon.com... > > "Daniel Yokomiso" <daniel_yokomiso@yahoo.com.br> wrote in message news:as1944$1e3u$2@digitaldaemon.com... > > Speaking about matrices, why the following code fails to link? > > It gives a message 'Error 42: Symbol Undefined __init_TypeInfo_Ai' > > Because I have failed to write a typeinfo for arrays of ints :-( See the ti*.d files in \dmd\src\phobos. > Ok. > > And are multidimensional slices valid? Id would be great to matrix > > programming, so we could write: > > A[1..3][4..10] += B[5..7][8..14]; > > Or something like that. > > I'd have to think about that. I don't think it will work for rectangular arrays. > I agree. Maybe some magic is needed :-). If D get a expressive Matrix type (my current work) with this kind of syntax maybe we won't need true rectangular arrays for 95% of its usage (my matrix provides data packed in column order). Blitz++ provides multi-dimensional slices and subarrays (http://www.oonumerics.org/blitz/manual/blitz02.html#l46) so its users will ask for it. > > Also overloading of the [] operator for > > subscript and slicing would be a good idea, together with the in operator. > > Yes, that's a good idea. > I find myself always writing a contains method, or writing a slice + set/at methods. If D has this capability maybe we can use it instead of STL-like iterators to provide generic programming. After all a slice is a pointer + length, very similar to an iterator (pointer + direction of transversal). > > And the last thing, I'm stuck with a problem of them current template > > syntax. Right now I have a template Matrix class defined for any kind of numeric type. But for some operations like the trace of the matrix (sum of > > the main diagonal elements) I need to have a zero value like: > > T trace() { > > T sum = 0; > > // code to sum the main diagonal elements > > return sum; > > } > > For TMatrix(int) or TMatrix(float) instances this code is ok, but when > I > > start using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now I can > > solve this by using a template variable to hold the value of zero and code > > verifications to ensure that the zero value is defined before the > programmer > > uses a Matrix. > > I don't understand. In the template code I must declare a variable of type T that is initialized to some value. If T is a user-defined numeric type that isn't addable to number 0 (after all matrix members need only to be addable to their kind). Think of matrices of type-safe quantities, like Mass, Length or Time. Neither of these types are addable to numbers, althought they're addable to themselves. The literal 0 is used to define int zero, not Length 0. So I use this template variables to allow the user to define the interesting numbers (zero and one, all others are defined by those). > > > instance TMatrix(Quatertion) tmatrix; > > tmatrix.zero = new Quatertion(); > > tmatrix.Matrix m = new tmatrix.Matrix(10, 10); > > > > where new Quatertion() is the zero value for quaternions. Haskell, ML and > > Ada allow any kind of parameters, besides types, when you instantiate a generic module. I think should follow its example, so our could would be something like: > > > > instance TMatrix(Quatertion, new Quatertion()) tmatrix; > > tmatrix.Matrix m = new tmatrix.Matrix(10, 10); > > That's probably a good idea. I wanted to try it with just types for starters > and see how far that got. > > > I keep thinking of it as a template constructor. Maybe we could push > > this template parametrization stuff to module level, and the programmer > > would use a template module as a regular module: > > > > > > module matrix(T, T zero); // declaration of template module > > > > import matrix(int, 0); > > import matrix(float, 0.0); > > import matrix(Quaternion, new Quaternion()); > > > > > > And use the module values without any particular need to refer to the > > instance variable. I think it's still time for a change like that, AFAIK > I'm > > the only one doing serious template work with D, and I can live with any change in the language. It would make the language simpler for template users (if we make matrix programming right we can get lots of people interested in doing scientific programming in D, it's my current goal). > > Ah, parameterized modules! Interesting shorthand! > I realized that most of the time I wrote one template per module, for clarity. Before I didn't see D modules as ML modules, capable of parametrization. I think we can drop the template concept and favour a more expressive module system. This is better for allows library writers, because they don't need to write: module collections.set; import collections.collection; template TSet(T) { private instance TCollection(T) tcollection; private alias tcollection.Collection Collection; public class Set : Collection { ... } } Instead they can write: module collections.set(T); import collections.collection(T); public class Set : Collection { ... } and live with less baggage to keep in their heads when trying to define a simple generic set class. Best regards, Daniel Yokomiso. "My opinions may have changed, but not the fact that I am right." - Ashleigh Brilliant |
November 27, 2002 Re: Templates and matrices | ||||
---|---|---|---|---|
| ||||
Posted in reply to Burton Radons | Reply embbeded. "Burton Radons" <loth@users.sourceforge.net> escreveu na mensagem news:as2d4u$2koh$1@digitaldaemon.com... > Walter wrote: [snip] > >> And are multidimensional slices valid? Id would be great to matrix > >>programming, so we could write: > >>A[1..3][4..10] += B[5..7][8..14]; > >> Or something like that. > > > > I'd have to think about that. I don't think it will work for rectangular arrays. > > I'll make the problem explicit. Given: > > int [10] [10] a; > > The resulting type from "a [5] [4]" is int, but the resulting type from "a [5 .. 10] [4 .. 5]" is int[][]. Indices remove a layer, but slicing doesn't, so a double-slice should still result in the same number of dimensions. > > Here's where they got in the discussion, Daniel. Pavel suggested the syntax: > > int [r, c] a; > > So if that's a matrix, I could get a column or a row by "a [0 .. r, c]" or "a [r, 0 .. c]", or get sub-matrices out of that. Powerful stuff, but hard on the compiler and really hard on operator overloading. Needs solid justification. > Blitz++ (http://www.oonumerics.org/blitz/manual/blitz02.html#l46) is a solid justification? :-) Jokes aside they have a very powerful slicing mechanism, runtime performance comparable to Fortran and use lots of C++ template magic. I don't mind the syntax for primitive matrices, but this kind of stuff is needed when someone want to write simple code to work with matrices. If you check the Fortran Linpack code (or the Java variant Jama), you'll lot's of explicit written loops for things like: // code snippet from Jama // V is a square n x n matrix for (int k = n-1; k >= 0; k--) { if ((k < nrt) & (e[k] != 0.0)) { for (int j = k+1; j < nu; j++) { double t = 0; for (int i = k+1; i < n; i++) { t += V[i][k]*V[i][j]; } t = -t/V[k+1][k]; for (int i = k+1; i < n; i++) { V[i][j] += t*V[i][k]; } } } for (int i = 0; i < n; i++) { V[i][k] = 0.0; } V[k][k] = 1.0; } that could be written as (or something like that): for (int k = n-1; k >= 0; k--) { if ((k < nrt) & (e[k] != 0.0)) { for (int j = k+1; j < nu; j++) { double t = sum(V[k + 1 .. n][k] * V[k + 1 .. n][j]); t = -t / V[k+1][k]; V[k + 1 .. n][j] += t * V[k + 1 .. n][k]; } } V[0..n][k] = 0.0; V[k][k] = 1.0; } Seven lines smaller and simpler to understand. Every time anyone writes a loop (matrix processing uses several nested loops usually) is because some abstraction was lost. With complete multi-dimentsional slicing we can do almost all loops with less code, and the compiler will know we want to do slice processing, not some generic loop that happens to be a slice. [snip] > >> And the last thing, I'm stuck with a problem of them current template > >>syntax. Right now I have a template Matrix class defined for any kind of numeric type. But for some operations like the trace of the matrix (sum of > >>the main diagonal elements) I need to have a zero value like: > >>T trace() { > >> T sum = 0; > >> // code to sum the main diagonal elements > >> return sum; > >>} > >> For TMatrix(int) or TMatrix(float) instances this code is ok, but when > > > > I > > > >>start using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now I can > >>solve this by using a template variable to hold the value of zero and code > >>verifications to ensure that the zero value is defined before the > > > > programmer > > > >>uses a Matrix. > > > > > > I don't understand. > > Take the code: > > T sum (T [] list) > { > T result = 0; > > for (T *c = list, e = c + list.length; c < e; c ++) > result += *c; > > return result; > } > > This is fine if T is int or float, but if it's "struct vec3 { float x, y, z; }" then the initial value of result doesn't make sense. > > Howsabout the builtin types have an attribute ".zero" that we can put into our user types as static variables/constants. Then that line would be: > > T result = T.zero; > > Which I think would work better than C++'s constructor method. zero could even be a static property, and if it's proveably const then the compiler can do lots with it. > This solution is also good, but we need also the .one attribute. But this solution is paliative, because it doesn't address the problem that there is more to generic programming than types. We can simulate this by introducing dummy classes and instances: template TNumerics(T, U) { private U attributes = new U(); T sumSqrt(T[] list) { T result = attributes.zero(); for (int i = 0; i < list.length; i ++) { result += attributes.sqrt(list[i]); } return result; } } This hack, not very ugly but a hack still. There are infinite possible parametrization of templates besides type and primitive values. Best regards, Daniel Yokomiso. "Confucius say... man who run in front of car get tired; man who run behind car get exhausted." |
November 27, 2002 Re: Templates and matrices | ||||
---|---|---|---|---|
| ||||
Posted in reply to Daniel Yokomiso | See below. Daniel Yokomiso wrote: > > Reply embedded > ... > > > And the last thing, I'm stuck with a problem of them current > template > > > syntax. Right now I have a template Matrix class defined for any kind of numeric type. But for some operations like the trace of the matrix (sum > of > > > the main diagonal elements) I need to have a zero value like: > > > T trace() { > > > T sum = 0; > > > // code to sum the main diagonal elements > > > return sum; > > > } > > > For TMatrix(int) or TMatrix(float) instances this code is ok, but > when > > I > > > start using it for user-defined classes that are not addable to 0, like Vector, Quaternion, Equation, etc. the code stops working. Right now I > can > > > solve this by using a template variable to hold the value of zero and > code > > > verifications to ensure that the zero value is defined before the > > programmer > > > uses a Matrix. > > > > I don't understand. > > In the template code I must declare a variable of type T that is > initialized to some value. If T is a user-defined numeric type that isn't > addable to number 0 (after all matrix members need only to be addable to > their kind). Think of matrices of type-safe quantities, like Mass, Length or > Time. Neither of these types are addable to numbers, althought they're > addable to themselves. The literal 0 is used to define int zero, not Length > 0. So I use this template variables to allow the user to define the > interesting numbers (zero and one, all others are defined by those). ... Sorry this reply sounds over-simplified, but for "accumulation" operations (summing, adding to a group, etc.) I generally use assignment for the first element/item, and accumulation for all subsequent iterations. It takes a little extra work to ensure all types passed as template parameters support this use (all native types do), and I find it eliminates the need for many explicit initializations. It can also handle non-existant and soltiary items. -BobC |
November 28, 2002 Re: Templates and matrices | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robert W. Cunningham | "Robert W. Cunningham" <FlyPG@users.sourceforge.net> escreveu na mensagem news:3DE55A59.2C9152BB@users.sourceforge.net... > See below. > > Daniel Yokomiso wrote: > > > > Reply embedded > > > ... > > > > And the last thing, I'm stuck with a problem of them current > > template > > > > syntax. Right now I have a template Matrix class defined for any kind of > > > > numeric type. But for some operations like the trace of the matrix (sum > > of > > > > the main diagonal elements) I need to have a zero value like: > > > > T trace() { > > > > T sum = 0; > > > > // code to sum the main diagonal elements > > > > return sum; > > > > } > > > > For TMatrix(int) or TMatrix(float) instances this code is ok, but > > when > > > I > > > > start using it for user-defined classes that are not addable to 0, like > > > > Vector, Quaternion, Equation, etc. the code stops working. Right now I > > can > > > > solve this by using a template variable to hold the value of zero and > > code > > > > verifications to ensure that the zero value is defined before the > > > programmer > > > > uses a Matrix. > > > > > > I don't understand. > > > > In the template code I must declare a variable of type T that is > > initialized to some value. If T is a user-defined numeric type that isn't > > addable to number 0 (after all matrix members need only to be addable to their kind). Think of matrices of type-safe quantities, like Mass, Length or > > Time. Neither of these types are addable to numbers, althought they're addable to themselves. The literal 0 is used to define int zero, not Length > > 0. So I use this template variables to allow the user to define the interesting numbers (zero and one, all others are defined by those). > ... > > Sorry this reply sounds over-simplified, but for "accumulation" operations (summing, adding to a group, etc.) I generally use assignment for the first element/item, and accumulation for all subsequent iterations. > > It takes a little extra work to ensure all types passed as template parameters support this use (all native types do), and I find it eliminates the need for many explicit initializations. It can also handle non-existant and soltiary items. > > > -BobC How do you handle empty lists with this scheme? Just curious. But this usage isn't useful in other contexts (eg. creating an identity matrix, you need explicit zeros and ones, or comparison to zero value). |
November 29, 2002 Re: Templates and matrices | ||||
---|---|---|---|---|
| ||||
Posted in reply to Daniel Yokomiso | Daniel Yokomiso wrote:
>
> "Robert W. Cunningham" <FlyPG@users.sourceforge.net> escreveu na mensagem news:3DE55A59.2C9152BB@users.sourceforge.net...
> > Sorry this reply sounds over-simplified, but for "accumulation" operations (summing, adding to a group, etc.) I generally use assignment for the first element/item, and accumulation for all subsequent iterations.
> >
> > It takes a little extra work to ensure all types passed as template parameters support this use (all native types do), and I find it eliminates the need for many explicit initializations. It can also handle non-existant and soltiary items.
>
> How do you handle empty lists with this scheme? Just curious. But this usage isn't useful in other contexts (eg. creating an identity matrix, you need explicit zeros and ones, or comparison to zero value).
In general, I always ensure all new "things" are initialized to null (zero, all bits clear, whatever). Generally, the compiler does this for me. So I get nice empty lists and zero matrices.
The vast majority of my matrix applications have been in the realm of image processing, where I'm more interested in initialization to a multiplicative "no-op" (pass-thru) operation than I am in any specific mathematical value (it is a property of the higher level algorithm and/or specific algebra, and not of the lower-level numeric type). So for many pixel-processing operational vectors, this would set all elements to one in the constructor/initializer, and for convolution matrices it would set only the diagonals to one.
Hmmm... I suppose I havent yet had the need to perform truly arbitrary algebraic operations using templates, where instantiation with ANY mathematical or numeric-like type should make sense. While arbitrary type instantiation works for things like collections, I can't think of any cases where it would need to work across algebras (such as with non-mathematical types), though your Haskell example of add() is a powerful one I'll be thinking about.
However, it seems to me that operations that are general in theory are not all that general in practice. That is to say, algebras based on "simple" types (integers, rationals, reals) are fundamentally different (while having a confusingly similar "feel") to algebras for more complex entities (complex numbers, quaternions, vectors, matrices, etc.). Only the most basic operations would map, and much of the power of the underlying type would not be generally accessible within such a template instantiation.
One thing I find I do with annoying frequency is to wrap simple numeric types in "true" object wrappers so they may be handled at a higher level. Languages that give such fundamental types (integers, floats, etc.) FULL (or close to full) object capabilities would, in general, be much more capable of doing what you describe. (Smalltalk, Ruby, etc.)
Rather than a problem with templates, aren't we really talking about limitations of the D type system? Shouldn't EVERYTHING be an object, at least from a syntactic perspective? (The compiler can implement the semantics any way it chooses: I doubt integers will need vtables, but I'd like to use object notation to access a set of fundamental methods, ones I can override in my own derived types.)
D has already started down this path for IEEE floats, extending the underlying physical representation to encompass "true" NAN support. I argued months ago for "smart integers" that, like D floats, could have explicit "uninitialized" or "Not-An-Integer" states/properties. This would allow integers to more easily participate in higher-level template use, as would any another object (having a mathematical/numeric/algebraic flavor).
I suppose I'm uneasy with OO languages have non-OO fundamental types. This seems to be an inherent limitation of all OO languages that try to retain the procedural programming model (C++, Java, C#, D, etc.). Adding the syntactic sugar to give ALL fundamental types full object properties may eliminate Daniel's concerns.
Or am I missing Daniel's point? Let me back up a bit and tell where I'm coming from:
I'm just a language practitioner, not a guru of any kind. I like simple language paradigms that have general (and powerful) application, and I detest special cases.
For example, I detest operator precedence rules, so I go out of my way to make them irrelevant in my code, using whatever level of parenthesis nesting is needed to relieve anyone looking at the code of any need to remember language-specific idiosynchracies. Were I to have my way, I'd eliminate nearly all operator precedence rules from D.
Another example: I detest implicit type conversion. I always use explicit casts to do what I want done. Again, the reader of the code will have no confusion, even if they are a language newbie. Were I to have my way, I'd eliminate all implicit type conversions from D.
Both of these techniques add NOTHING to the compile time, and cause no difference in the generated code (optimizers are usually very smart these days). But I've seen misunderstood precedence rules and unexpected implicit conversions cause "stupid" coding mistakes that can ben needlessly difficult to find.
Computer languages are NOT for computers: They are for computer programmers, for people. My code gets reviewed by my peers, by customers and partners, and by our legal department. I want it to be as unambiguous as possible. (This has nothing at all to do with "simple" code: My code is often VERY complex!) (My code is also used to train the people who will maintain it, so I can move on to new and exciting projects. If they can't understand it, I will be the one to maintain it.)
Continuing this desire for uniformity: I want all types (fundamental and abstract) to have similar behavior, at least in the syntactical domain. If you have an OO paradigm, then use it everywhere. No special cases or limitations or dependencies! Again, such a paradigm would need to add NOTHING to compile time or code size/complexity. It is all just notation. For "fundamental" types it need be nothing more than syntactic sugar that maps to standard conventions or library calls.
Templates are a notation with some peculiar characteristics, in that their "types" are actually meta-types (becoming concrete only upon instantiation). Template instantiation parameters should be as uniform as possible, in that all such types should possess a minimum set of common properties/capabilities (at least from the perspective of their use as template instantiation parameters). This is not the case in D, hence the problems instantiating a template that performs algebraic operations with an int or a matrix or any other mathematical type or entity.
From my practitioner's perspective, the template use Daniel describes is intended to be for an algebraic operation that should be valid on all types that are sufficiently "numeric-like", from integers to matrices to quaternions to whatever. While the operation itself may indeed be valid (from a math-theoretical perspective, within a given algebra or set of algebras), the limitations of D's underlying type system makes a truly general implementation impractical.
I don't see this as a problem with templates: Isn't the problem Daniel describes really more a problem with types in D? (With "mathematical" types in templates, in this particular case.)
-BobC
|
Copyright © 1999-2021 by the D Language Foundation