digitalmars.D.learn - Creating Structs/Classes at runtime?
- Brian Brady (95/95) Feb 17 2013 Hi
- Adam D. Ruppe (27/27) Feb 17 2013 There is a way to get what you described, and your code is fairly
- Brian Brady (20/30) Feb 17 2013 doesn't this writefln() assume that I know what the Variables are
- H. S. Teoh (17/25) Feb 17 2013 [...]
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (36/52) Feb 17 2013 Looking at the sample file you provide, what you call "variables" look
- Brian Brady (20/29) Feb 18 2013 On Monday, 18 February 2013 at 05:26:39 UTC, Ali Çehreli wrote:
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (9/16) Feb 18 2013 You mean like this?
- Brian Brady (22/30) Feb 18 2013 I don't mind reading them all in as strings, and then converting
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (64/72) Feb 18 2013 How about an OO solution?
- Brian Brady (9/10) Feb 18 2013 That looks awesome. :)
- bearophile (6/12) Feb 18 2013 Since some time I am maintaining most of the D code on
- Brian Brady (13/18) Feb 18 2013 Apologies. My wording was poor. I believe the RosettaCode code
Hi Ok first ... is this possible? Or wise? Being mostly self taught with regards to programming I sometimes wonder if the way I'm going about something is even the right approach, never mind whether my code is logical. So if this seems ludicrous then please tell me my approach is silly and point me in the right direction ... :) Ok so ... I was reading in some data from a csv and wanted to read it into a struct called. My problem is 1) my full csv is 4000 variables wide, and I really don't want to declare each variable in the struct (I've created a smaller one to test) 2) what if I wanted to read in different csvs of different sizes, I don't want to have to change my program each time? After much googling, I thought I found something on RosettaCode that would do what I wanted (http://rosettacode.org/wiki/Add_a_variable_to_a_class_instance_at_runtime#D) but when I try to use it, it doesn't work. (Its actually adding to a class, which may be better than a struct in this instance, but regardless, it doesn't work) As my programming skills are mainly obtained through finding other work that does something similar to what I desire and stealing/hacking it, please be gentle if there is some glaringly foolish mistake that is obvious. :S Thanks in advance Brian My code is: module main; import std.stdio; import std.csv; import std.string; import std.file; import std.array; import std.variant; class dataVector(T) { private T[string] vars; property T opDispatch(string key)() pure nothrow { return vars[key]; } property void opDispatch(string key, U)(U value)/*pure*/ nothrow { vars[key] = value; } }; void fillVector(int size, string filenameToUse) { writeln(size); uint loopNum = size; do { writeln(loopNum); // auto test = dataVector!Variant(); /* auto file = File(filenameToUse, "r"); foreach(line;file.byLine()) { foreach(ob;csvReader!Data(text)) { dataVector.size ~= ob; } }*/ // **************************************** // according to the RosettaCode this is how I should be able // to create a variable a in the class test with a value of loopNum + 1 // **************************************** test.a = loopNum + 1; // writeln(test.a); loopNum--; } while(loopNum != 0); } void main() { string input; string filename = "/A/Path/To/The/Dark/Side/output.csv"; auto file = File(filename, "r"); /* this is super dynamic, but I've managed to create a different function to calculate this, which I have omitted to keep the code concise */ int numOfVars = 11; if (numOfVars > 0) fillVector(numOfVars, filename); } The csv I would eventually like to read in is of the form: test1,303,-140,-166,-317,-310,414,-157,-360,-120,-89 test10,-1,70,-57,-101,112,137,-134,9,195,86 test100,367,78,-417,123,220,-234,-170,236,-218,-351 test1000,309,-178,-674,-202,514,218,-165,76,-82,-328 test10000,-131,142,6,-143,80,46,29,48,-84,-113
Feb 17 2013
There is a way to get what you described, and your code is fairly close... but there's a much simpler way too. from the std.csv docs: == When an input contains a header the $(D Contents) can be specified as an associative array. Passing null to signify that a header is present. ------- auto text = "Name,Occupation,Salary\r" "Joe,Carpenter,300000\nFred,Blacksmith,400000\r\n"; foreach(record; csvReader!(string[string]) (text, null)) { writefln("%s works as a %s and earns $%s per year.", record["Name"], record["Occupation"], record["Salary"]); } ------- == The associative array directly is the simplest way to get this to work. All items read will be of type string if you do it like this, and you access with the [] syntax. You can convert to other types with to when you want to use it: import std.conv; int salary = to!int(record["salary"]); and that's the simplest way to make it work.
Feb 17 2013
On Monday, 18 February 2013 at 00:52:12 UTC, Adam D. Ruppe wrote: [...]------- auto text = "Name,Occupation,Salary\r" "Joe,Carpenter,300000\nFred,Blacksmith,400000\r\n"; foreach(record; csvReader!(string[string]) (text, null)) { writefln("%s works as a %s and earns $%s per year.", record["Name"], record["Occupation"], record["Salary"]); }doesn't this writefln() assume that I know what the Variables are called? ie "Name", "Occupation", etc.? I want to be able to run the same program against a file with 4 variables or a file with 400 variables, so specifying names wouldn't work. Can I somehow use a record[var[a]] where a can be a number between 0 and the count of variables? Also, if I wanted to store this as I read it in from csvReader, how can I define the array? It'll be of differing sizes if my 2 csv's are of different sizes. This kinda the crux of my problem. string[string] Data; is my first guess based on http://dlang.org/hash-map.html but then this doesn't work: foreach(line;file.byLine()) foreach(ob;csvReader!Data(line)) { // do things }
Feb 17 2013
On Mon, Feb 18, 2013 at 01:44:49AM +0100, Brian Brady wrote: [...]I was reading in some data from a csv and wanted to read it into a struct called. My problem is 1) my full csv is 4000 variables wide, and I really don't want to declare each variable in the struct (I've created a smaller one to test) 2) what if I wanted to read in different csvs of different sizes, I don't want to have to change my program each time?[...] It seems to me that what you need is not any struct or class per se, but a runtime container that stores different kinds of data depending on runtime input. I would recommend using an associative array instead of trying to shoehorn dynamic data into struct fields. Say something like: import std.variant; Variant[string] data; data[fieldName] = value; ... auto myField = data[fieldName]; ... Etc. T -- There is no gravity. The earth sucks.
Feb 17 2013
On 02/17/2013 04:44 PM, Brian Brady wrote:1) my full csv is 4000 variables wide, and I really don't want to declare each variable in the struct (I've created a smaller one to test)Looking at the sample file you provide, what you call "variables" look like data points.2) what if I wanted to read in different csvs of different sizes, I don't want to have to change my program each time?Then you need something other than a CSV reader. Ordinarily, records in a CSV files have a known number of fields.After much googling, I thought I found something on RosettaCode that would do what I wanted(http://rosettacode.org/wiki/Add_a_variable_to_a_class_instance_at_runtime#D)but when I try to use it, it doesn't work. (Its actually adding to a class, which may be better than a struct in this instance, but regardless, it doesn't work)That is not a natural idiom for D and I don't think it is needed here. :)The csv I would eventually like to read in is of the form: test1,303,-140,-166,-317,-310,414,-157,-360,-120,-89 test10,-1,70,-57,-101,112,137,-134,9,195,86 test100,367,78,-417,123,220,-234,-170,236,-218,-351 test1000,309,-178,-674,-202,514,218,-165,76,-82,-328 test10000,-131,142,6,-143,80,46,29,48,-84,-113What I see there is a label and a number of integers. Here is a simple parser that takes advantage of the %( and %) grouping format specifiers: import std.stdio; import std.format; struct Data { string label; int[] values; } int main(string[] args) { if (args.length != 2) { stderr.writefln("Usage: %s <input-file-name>", args[0]); return 1; } auto file = File(args[1], "r"); Data[] data; foreach (line; file.byLine) { string label; int[] values; formattedRead(line, "%s,%(%s,%)", &label, &values); data ~= Data(label, values); } writeln(data); return 0; } Ali -- D Programming Language Tutorial: http://ddili.org/ders/d.en/index.html
Feb 17 2013
On Monday, 18 February 2013 at 05:26:39 UTC, Ali Çehreli wrote: Thank you for the working solution. [...]Looking at the sample file you provide, what you call "variables" look like data points.Yes, apologies. Different languages using different terms to describe, essentially the same thing. [...]Then you need something other than a CSV reader. Ordinarily, records in a CSV files have a known number of fields.While you would assume the lines within a csv contain a specific number of 'data points' surely the width of the csv is as variable as the data within them? This is what I want to code around. [...]That is not a natural idiom for D and I don't think it is needed here. :)That's ok. Like I said, I open to be pointed in a new, better direction. [...]What I see there is a label and a number of integers. Here is a simple parser that takes advantage of the %( and %) grouping format specifiers:Aye, in this instance it is just that, but I was looking for a more generalised code to account for, for example, when the first 2 are labels, or the first and third ... Thanks everyone for the help. Regards Brian
Feb 18 2013
On 02/18/2013 02:55 AM, Brian Brady wrote:On Monday, 18 February 2013 at 05:26:39 UTC, Ali Çehreli wrote:You mean like this? 10,20,label,1,2,3,4 Then what are 10 and 20 on that line? Do they belong to the previous label? If so, I think this format is too free-form to be parsed by a general solution like csvReader. It looks like something special needs to be done and it is hard to say without knowing the meanings of 10 and 20 above. :) AliWhat I see there is a label and a number of integers. Here is a simple parser that takes advantage of the %( and %) grouping format specifiers:Aye, in this instance it is just that, but I was looking for a more generalised code to account for, for example, when the first 2 are labels, or the first and third ...
Feb 18 2013
On Monday, 18 February 2013 at 16:54:59 UTC, Ali Çehreli wrote: [...]You mean like this? 10,20,label,1,2,3,4 Then what are 10 and 20 on that line? Do they belong to the previous label? If so, I think this format is too free-form to be parsed by a general solution like csvReader. It looks like something special needs to be done and it is hard to say without knowing the meanings of 10 and 20 above. :) AliI don't mind reading them all in as strings, and then converting them. I imagine I can do that relatively easily (famous last words) by checking if all the entries in the string are numeric and if so casting to an int/long/double etc. My question was aimed at how to store them really. What was the best dynamic container type that I could use to store data points read from csvs. I want something which is robust enough that I don't have to keep changing the code every time I change my csv. So a program that reads in a csv of the form: "string, number, number, number" and stores it in a container/matrix/associative array could also read in "number, number, string, number, string, number, number, number" and store it in a container with similar properties. Once I can get both csvs input in the same manner, I can work on determining what is in each container, and go from there. While this may seem madness, I hope my explanation describes what I am trying to achieve? Brian
Feb 18 2013
On 02/18/2013 04:31 PM, Brian Brady wrote:So a program that reads in a csv of the form: "string, number, number, number" and stores it in a container/matrix/associative array could also read in "number, number, string, number, string, number, number, number" and store it in a container with similar properties. Once I can get both csvs input in the same manner, I can work on determining what is in each container, and go from there.How about an OO solution? import std.stdio; interface Data { void doSomething(); } // This works with any simple type class SimpleData(T) : Data { T value; this(T value) { this.value = value; } void doSomething() { writefln("Doing something with %s %s", T.stringof, value); } } alias IntData = SimpleData!int; alias StringData = SimpleData!string; // My special type struct MyStruct { double d; string s; void foo() { writefln("Inside MyStruct.foo"); } } // This works with my special type class MyStructData : Data { MyStruct value; this(double d, string s) { this.value = MyStruct(d, s); } void doSomething() { writefln("Doing something special with this MyStruct: %s", value); value.foo(); } } void main() { Data[] dataContainer; // Append according to what gets parsed from the input dataContainer ~= new IntData(42); dataContainer ~= new StringData("hello"); dataContainer ~= new MyStructData(1.5, "goodbye"); // Use the data according to the Data interface foreach (data; dataContainer) { data.doSomething(); } } The output: Doing something with int 42 Doing something with string hello Doing something special with this MyStruct: MyStruct(1.5, "goodbye") Inside MyStruct.foo Ali
Feb 18 2013
On Tuesday, 19 February 2013 at 01:09:06 UTC, Ali Çehreli wrote: [...]How about an OO solution?That looks awesome. :) I'll have a play with it and see if I can bend it to my will. Thanks for the help. Like I said, I'm a bit of a noob, so a push in a more suitable direction is always appreciated. :) Regards Brian
Feb 18 2013
Brian Brady:After much googling, I thought I found something on RosettaCode that would do what I wanted (http://rosettacode.org/wiki/Add_a_variable_to_a_class_instance_at_runtime#D) but when I try to use it, it doesn't work. (Its actually adding to a class, which may be better than a struct in this instance, but regardless, it doesn't work)Since some time I am maintaining most of the D code on Rosettacode. What's broken in that program? "it doesn't work" is too much vague. Bye, bearophile
Feb 18 2013
On Monday, 18 February 2013 at 12:28:22 UTC, bearophile wrote: [...]Since some time I am maintaining most of the D code on Rosettacode. What's broken in that program? "it doesn't work" is too much vague. Bye, bearophileApologies. My wording was poor. I believe the RosettaCode code worked for that example, but when I tried to get it to work for my purposes I could not, and could not figure out why. Possibly because I was also trying to incorporate reading from a file at the same time. To be honest, I'm not really sure what is going on in the struct definition to even know where my code was possibly failing. I'd like to take time to figure it out, but it has a lot of parts that I don't understand, so it'll be a while. Cheers Brian
Feb 18 2013