digitalmars.D.learn - Calculating/Averaging over a struct value
- Brian Brady (55/55) Mar 21 2012 All
- Timon Gehr (44/44) Mar 21 2012 #! /usr/bin/rdmd
- bearophile (5/6) Mar 21 2012 This is a quite natural syntax, and I think it's handy, but it's
- David Nadlinger (4/8) Mar 21 2012 I don't think this will ever be supported (without using
- bearophile (6/9) Mar 21 2012 Right, right, sorry, I meant a compile time argument.
- Brian Brady (2/2) Mar 21 2012 Thanks for the replies.
- Jesse Phillips (24/28) Mar 21 2012 Timon has given you an good example to get D to generate some
- Jesse Phillips (12/16) Mar 21 2012 Nevermind, just needed to map
All This might be relatively trivial so please point me at documentation to read if it is. I am creating an array of Structs(is this the best thing to do) as per the example below. import std.array; import std.csv; import std.stdio; import std.string; struct Data { string Date; double d1; double d2; int i4; } double average(Data[] x, string para) { if (para != "d1") return -1; else { // Here I want to dynamically average over whatever was entered as para // but if I write x[].para if throws an error foreach(i;0 .. x[].length) writeln("bob"); return 1; } } void main() { Data[] allData; string text= "2011-11-30,2.25,3.00,100\n2011-11-29,2.24,2.75,1000\r\n"; foreach(ob;csvReader!Data(text)) { allData ~= ob; } writeln(allData); string input; writeln("Enter the variable you would like to average over: "); stdin.readln(input); writeln(input); } As per the commented section, I want to be able to dynamically figure out, which member of the struct to average across, for all the structs in the array. Is there any way of doing this, or will I have to code one for each possibility. ie. if(para = "d1") ... calculate the average of x[].d1 else if(para = "d2") ... calculate the average of x[].d2 Is there a better method of doing this that I can look at? Thanks in advance. Brian
Mar 21 2012
import std.array; import std.csv; import std.stdio; import std.string; struct Data{ string Date; double d1; double d2; int i4; } double average(Data[] x, string para){ double result = 0.0; theswitch:switch(strip(para)){ foreach(member;__traits(allMembers, Data)){ case member: static if(__traits(compiles, {result+=mixin(`x[0].`~member);})){ foreach(d; x){ result+=mixin(`d.`~member); } }else{ throw new Exception("don't know how to average over "~para); } break theswitch; } default: throw new Exception("no member named "~para); } return result/x.length; } void main(){ Data[] allData; string text= "2011-11-30,2.25,3.00,100\n2011-11-29,2.24,2.75,1000\r\n"; foreach(ob;csvReader!Data(text)){ allData ~= ob; } writeln(allData); string input; writeln("Enter the variable you would like to average over: "); stdin.readln(input); try writeln(average(allData,input)); catch(Exception e){writeln(e.msg);} }
Mar 21 2012
Brian Brady:// but if I write x[].para if throws an errorThis is a quite natural syntax, and I think it's handy, but it's not supported yet. Bye, bearophile
Mar 21 2012
On Wednesday, 21 March 2012 at 18:11:11 UTC, bearophile wrote:Brian Brady:I don't think this will ever be supported (without using opDispatch) – para is a runtime value here… David// but if I write x[].para if throws an errorThis is a quite natural syntax, and I think it's handy, but it's not supported yet.
Mar 21 2012
David Nadlinger:I don't think this will ever be supported (without using opDispatch) – para is a runtime value here… DavidRight, right, sorry, I meant a compile time argument. And even that, it's not clear what arr[].foo means. Maybe a lazy Range of that field, that is a lazy column. Bye, bearophile
Mar 21 2012
Thanks for the replies. Timons reply answers my question ... now I just have to figure out how :P
Mar 21 2012
On Wednesday, 21 March 2012 at 17:02:05 UTC, Brian Brady wrote:As per the commented section, I want to be able to dynamically figure out, which member of the struct to average across, for all the structs in the array.Timon has given you an good example to get D to generate some code for you. I would also point out that combining your if block or even Timon's solution with a template: double average(string param)(Data[] x) { static if (!isNumeric!(typeof(mixin("Data."~param)))) return -1; else { //auto average = reduce!("a."~param~" + b."~param)(x)/x.length; typeof(return) average = 0; foreach(v; x) { mixin("average += v."~param~";"); } return average/x.length; } } Note, I commented out reduce as it uses the array type instead of the calculation type. I think I'll file than as a bug. Also the reduce version would not give you a double back even if it did work, need a cast in there. Now calculating the parameter is just auto ans = average!"d1"(x);
Mar 21 2012
On Wednesday, 21 March 2012 at 20:13:37 UTC, Jesse Phillips wrote:Note, I commented out reduce as it uses the array type instead of the calculation type. I think I'll file than as a bug. Also the reduce version would not give you a double back even if it did work, need a cast in there.Nevermind, just needed to map double average(string param)(Data[] x) { static if (!isNumeric!(typeof(mixin("Data."~param)))) return -1; else { return cast(double)reduce!("a + b")(map!("a."~param)(x))/x.length; } }
Mar 21 2012