digitalmars.D.learn - How to return range constructs?
- Robin (72/72) Feb 28 2014 Hiho,
- Dicebot (29/29) Feb 28 2014 Looks like you are over-complicating things:
- Robin (20/20) Feb 28 2014 Hiho,
- Robin (17/17) Feb 28 2014 Hiho,
- anonymous (12/26) Feb 28 2014 Don't worry, there is none. Replying to yourself is proper
- Mike Parker (43/47) Feb 28 2014 Actually, when using code that works with interface types, they
- Robin (10/10) Mar 01 2014 Hiho,
Hiho, for experimental and learning reasons I am working on a simple matrix library for D. And since ranges seem to be a cool feature of the D language I am currently trying to understand them and use them more in my codes. So I thought that it would be a nice feature if I could create methods to return specific ranges for certain matrix instances, e.g. something like getRowRange(size_t row) or getColumnRange(size_t col) which returns a range to easily iterate through a single row- or column vector of a matrix instance with a simple foreach loop. I store the matrix data within a single dimensional array for performance purposes instead of a jagged array: private { T[] data; Dimension dim; } The Dimension 'dim' is an object holding the number of rows and columns as well as some handy methods and properties such as isSquare etc. So my question is: How to define such a method which returns a range for a single row or column vector? The range should be modifiable depending on whether the matrix instance is const/immutable or not. Or isn't that possible at all? I thought that it could be tricky for row vectors since their data wouldn't be dense in memory. I've looked into the std.range documentation but doesn't seem to get a clue how to get this working. And if it works - what range type would fit best for my purposes - or do I need to create a sub-class of a certain range type specially fit for my matrix? My try so far: module neoLab.core.ColumnVectorRange; import std.range; import neoLab.core.Matrix; final class ColumnVectorRange(T) : RandomAccessFinite!T { private { T[] data; size_t cur = 0; size_t length = 0; } this(const ref Matrix m, size_t row) in { assert(row < m.getDimension().rows, "Row index is invalid."); } body { this.data = m.data; this.length = m.getDimension().cols; } ColumnVectorRange!T save() { // What does this do? } override T opIndex(size_t index) { return this.data[index]; // this correct? } override T moveAt(size_t index) { return this.data[this.cur]; // this good? } override size_t length() { return this.length; } override alias opDollar = this.length; // Is this nessecary? override ColumnVectorRange!E opSlice(size_t, size_t) { // What does this do in particular? } } I highly appreciate tips and answers! =) Thanks in advance. Robin
Feb 28 2014
Looks like you are over-complicating things: http://dpaste.dzfl.pl/eaca1408dc88 import std.range; struct ColumnVectorForwardRange(T) { private { T[] data; size_t cur = 0; size_t length = 0; } ColumnVectorForwardRange!T save() property { return this; } bool empty() property { return !length; } T front() property { return data[cur]; } void popFront() { ++cur; --length; } } static assert(isForwardRange!(ColumnVectorForwardRange!int)); void main() {}
Feb 28 2014
Hiho, ok thanks - I haven't known that interfaces are not a "must extend" like in java to serve as a real interface to something. This is strange to me in first place but has its pros. And when I want to build up a RandomAccessFinite I just have to implement the other methods as well, okay. :D Is this performance intense to iterate over these custom ranges instead of iterating plainly without them or is the compiler optimizing the overhead away? I have got another problem with my current implementation. My data field is private (as you can see from my first post) and I don't really want to create a public getter for it (because of the encapsulation) but I either don't want to copy it everytime I create a range for my matrix. On top of all I want to have seperate source files instead of one huge file (one module) where I can access everything (which is what I would need atm). So is there another way to solve this problem? Or do I really have to merge all my files into one big giantic file so that they are all in the same module? Robin
Feb 28 2014
Hiho, sorry for the double post but I can't find an edit button. I have managed to write a custom ForwardRange based on your code as I wished it to behave. However, the assertion fails on compilation, now. The only thing I have changed is that I have added a constructor as well as changing the type of T[] to Matrix!T pointer type. The general layout stayed the same and I don't think that a constructor may break the ForwardRange interface. Do you know what's wrong here? Here is the code: http://dpaste.dzfl.pl/8718b09cb825 Besides that ... are there nicer workarounds to prevent using matrix by value instead of storing it as a pointer which is kind of unsafe? Thanks in advance! Robin
Feb 28 2014
On Friday, 28 February 2014 at 23:06:24 UTC, Robin wrote:sorry for the double post but I can't find an edit button.Don't worry, there is none. Replying to yourself is proper etiquette around here.I have managed to write a custom ForwardRange based on your code as I wished it to behave. However, the assertion fails on compilation, now. The only thing I have changed is that I have added a constructor as well as changing the type of T[] to Matrix!T pointer type. The general layout stayed the same and I don't think that a constructor may break the ForwardRange interface. Do you know what's wrong here? Here is the code: http://dpaste.dzfl.pl/8718b09cb825Lines 43, 44: --- static assert(isForwardRange!(ColumnVectorForwardRange!double(new Matrix!double(5, 5), 2))); //static assert(isForwardRange!(ColumnVectorForwardRange!double)); --- isForwardRange works on types, not values. The commented version is correct.Besides that ... are there nicer workarounds to prevent using matrix by value instead of storing it as a pointer which is kind of unsafe?Make it a class.
Feb 28 2014
Hiho, I have made matrix a struct for a better performance and since everybody here on the forums was complaining about that it should be a struct type. When I uncomment the currently commented assertion I get linking errors. (these strange and nearly unreadable error messages.) Here is a part of them: ColumnVectorForwardRange.o: In function `_D6neoLab4core6Matrix13__T6MatrixTdZ6Matrix6__ctorMFNaNbNcxmxmxbZS6neoLab4core6Matrix13__T6MatrixTdZ6Matrix': neoLab/core/Matrix.d:(.text._D6neoLab4core6Matrix13__T6MatrixTdZ6Matrix6__ctorMFNaNbNcxmxmxbZS6neoLab4core6Matrix13__T6Mat ixTdZ6Matrix+0x31): undefined reference to `_D6neoLab4core9Dimension9Dimension6__ctorMFNaNbNcxmxmZS6neoLab4core9Dimension9Dimension' ColumnVectorForwardRange.o: In function ... many many many lines of error messages ... ColumnVectorForwardRange.o: In function `_D6neoLab4core6Matrix13__T6MatrixTdZ6Matrix6randomFxmxmdddZS6neoLab4core6Matrix13__T6MatrixTdZ6Matrix': neoLab/core/Matrix.d:(.text._D6neoLab4core6Matrix13__T6MatrixTdZ6Matrix6randomFxmxmdddZS6neoLab4core6Matrix13__T6Matr xTdZ6Matrix+0x145): undefined reference to `_D6neoLab4core9Dimension9Dimension4sizeMxFNaNbNdZm' collect2: error: ld returned 1 exit status --- errorlevel 1 Robin
Feb 28 2014
On Friday, 28 February 2014 at 23:53:48 UTC, Robin wrote:I have made matrix a struct for a better performance and since everybody here on the forums was complaining about that it should be a struct type.Making it a class is just a simple way to avoid the (explicit) pointer. Weigh that against your other goals and decide. Also, a pointer in itself isn't scarily unsafe.When I uncomment the currently commented assertion I get linking errors. (these strange and nearly unreadable error messages.) Here is a part of them:[...]undefined reference to `_D6neoLab4core9Dimension9Dimension6__ctorMFNaNbNcxmxmZS6neoLab4core9Dimension9Dimension'"undefined reference" means the linker can't find that symbol. That means, you forgot to pass some file in. When compiling and linking in one step, dmd needs all source files: something like `dmd neoLab/core/ColumnVectorForwardRange.d neoLab/core/Matrix.d neoLab/core/Dimension.d` (plus any other imported files outside of phobos). rdmd is a tool that reads the imports, calls dmd on all source files, and then runs the executable. It's shipped with dmd. With rdmd it would be just `rdmd neoLab/core/ColumnVectorForwardRange.d`.
Feb 28 2014
On Friday, 28 February 2014 at 19:02:24 UTC, Robin wrote:Hiho, ok thanks - I haven't known that interfaces are not a "must extend" like in java to serve as a real interface to something. This is strange to me in first place but has its pros.Actually, when using code that works with interface types, they are just like Java. // API interface Foo { ... } void doSomethingWithAFoo( Foo foo ); // User code class FooImpl : Foo { ... } But if this is what the API looks like, it's restricting all Foos to be classes, since in D structs can't extend an interface. Sometimes, that's exactly what you want to do, but in other cases it could make sense to allow structs as well. This allows more flexibility for the user to choose between heap vs. stack, for example. In D, this can be accomplished by using compile-time constructs to determine if a type implements a particular interface. So you could do away with the Foo interface completely and have something like this. // API // A convenient way to test if a type is a Foo template isFoo( T ) { enum isFoo = // use compile-time features to test for expected methods } // One way to use isFoo... void doSomethingWithAFoo( T )( T foo ) if( isFoo!T ) { ... } // Or, alternatively... void doSomethingWithAFoo( T )( T foo ) { static if( isFoo!T ) { ... } else { static assert( false, T.stringof ~ " doesn't implement all Foo methods!"); } } // User code class FooImplA { ... } struct FooImplB { ... } As long as both A & B implement all of the methods expected of a Foo, the template will match and the code will compile. To cover as many use-cases as possible, which Phobos probably should, it's useful to have both an object-based interface (interface Foo) and the template-based structural interface (isFoo). When an API need not be that all-encompassing, one or the other approach is enough.
Feb 28 2014
Hiho, anonimous: Thank you for that hint with rdmd, I was already wondering what that was. Mike Parker: Thank you very much for clearing that up for me. I really needed such a clear answer about this topic. So D language is highly advanced in meta programming and I should really begin to use it. =) Robin
Mar 01 2014