digitalmars.D.learn - Efficient enum array keys?
- Julian (118/118) Apr 10 2019 Hello,
Hello, When reading through the following D blog post, I noticed in the feature chart that D had "Arrays beginning at arbitrary indices" as a +1 feature, the same as in Ada. https://dlang.org/blog/2018/06/20/how-an-engineering-company-chose-to-migrate-to-d/ That surprised me, and from the code with the blog, that seems to be generous. In Ada you can just say Silly : array (2 .. 7) of Integer; to stack-allocate a six-integer array, with the first accessible integer at Silly(2) and the last integer at Silly(7). Meanwhile the blog has some epcomat thing that provides this to D: alias t = StaticArray!(int, 2, 20); t a; for (n = 2; n <= 20; n++) a[n] = n; That 'Silly' array isn't very serious. I got a lot more use out of Ada's feature of any discrete type being usable as an array index. This gives you the efficiency of normal arrays but with a huge readability boost. For an example from last year's Advent of Code: N : constant Natural := 32; type Runestring is array (1 .. N) of Rune; Maze : array (1 .. N) of Runestring := ... more of this ...); subtype Living_Runes is Rune range 'G' .. 'E'; Initial_States_Table : constant array (Living_Runes) of Unit_State := ('G' => (Hit_Points => 200, Attack_Power => 3, Moved => False), 'E' => (Hit_Points => 200, Attack_Power => 3, Moved => False)); So you can refer to Initial_States_Table('G').Hit_Points to know how healthy the goblins start out in this game. Thinking of that, I came up with the following D code: import std.stdio, core.exception; enum Days { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday } void main() { int[Days] worklog; ++worklog[Days.Wednesday]; writeln("- worklog"); dumplog(worklog); writeln(); int[Days.max+1] altlog; ++altlog[Days.Saturday]; writeln("- altlog"); dumplog(altlog); } void dumplog(T)(T log) { foreach (day; Days.Sunday .. Days.Saturday) { try { writefln("%5d %s", log[day], day); } catch (core.exception.RangeError e) {} } } Which has this output: - worklog 1 Wednesday - altlog 0 Sunday 0 Monday 0 Tuesday 0 Wednesday 0 Thursday 0 Friday And which has these faults, vs. the Ada equivalent: 1. worklog is an associative array and ++worklog[Days.Wednesday] compiles to a function call. This is more flexible of course but here it's unwanted and more expensive than a simple array. 2. worklog needs explicit initialization 3. this is ugly: int[Days.max+1] altlog 4. I can't write foreach (day; Days) { } ? 5. the foreach in that code is wrong: it skips Saturday, and the obvious fix of +1 is both ugly and an error: Error: cannot implicitly convert expression day of type int to Days This works: foreach (day; Days.min .. Days.max+1) writefln("%5d %s", log[cast(Days) day], cast(Days) day); But compare to Ada: with Ada.Text_IO; use Ada.Text_IO; procedure Enum is type Weekdays is (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday); procedure What_Day (Day : Weekdays) is begin case Day is when Monday | Tuesday | Wednesday | Thursday | Friday => Put_Line (Weekdays'Image (Day) & " is a weekday"); when Saturday | Sunday => Put_Line (Weekdays'Image (Day) & " is a weekend day"); end case; end What_Day; begin for J in Weekdays'Range loop What_Day (J); end loop; end Enum; ... which, OK, has its own problems: MONDAY is a weekday TUESDAY is a weekday WEDNESDAY is a weekday THURSDAY is a weekday FRIDAY is a weekday SATURDAY is a weekend day SUNDAY is a weekend day Is there a nicer way to have enum array keys in D?
Apr 10 2019
On Thursday, 11 April 2019 at 06:20:05 UTC, Julian wrote:Hello, When reading through the following D blog post, I noticed in the feature chart that D had "Arrays beginning at arbitrary indices" as a +1 feature, the same as in Ada. https://dlang.org/blog/2018/06/20/how-an-engineering-company-chose-to-migrate-to-d/ That surprised me, and from the code with the blog, that seems to be generous. [...] Is there a nicer way to have enum array keys in D?No. I've myself written my own EnumIndexedArray [1] type. It's pretty simple. Just a couple of operator overload to preovide the syntax. I went from ObjFPC/Delphi which has what you describe from Ada too and missed it. (typically: `enum TStuff = (); var stuffStrings: array[TStuff] of string;` ...) [1] https://github.com/Basile-z/iz/blob/9ce6fc0e2e0c74f97d530ce598a6842b7b048f25/import/iz/enumset.d#L1086
Apr 10 2019
On Thursday, 11 April 2019 at 06:45:23 UTC, Basile B. wrote:On Thursday, 11 April 2019 at 06:20:05 UTC, Julian wrote:Thanks. That still seems like enough work that I'd rather do things the D way. At least if I don't also want Enum sets. That gave me the idea for this though: import std.stdio; struct EnumRange(E) { int begin = E.min; int end = E.max + 1; bool empty() { return begin == end; } void popFront() { ++begin; } E front() { return cast(E) begin; } } enum Days { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday } void main() { int[Days.max+1] worklog; ++worklog[Days.Saturday]; writeln("- worklog"); EnumRange!(Days) why; foreach (day; why) writefln("%5d %s", worklog[day], day); } Which I'm still disappointed is not: foreach (day; EnumRange!(Days)) Also, this isn't too bad: void main() { int[Days.max+1] worklog; ++worklog[Days.Saturday]; writeln("- worklog"); foreach (day, count; worklog) writefln("%5d %s", count, cast(Days) day); } I don't see a difference in micro-benchmarks. *shrug*Is there a nicer way to have enum array keys in D?No. I've myself written my own EnumIndexedArray [1] type. It's pretty simple. Just a couple of operator overload to preovide the syntax. I went from ObjFPC/Delphi which has what you describe from Ada too and missed it. (typically: `enum TStuff = (); var stuffStrings: array[TStuff] of string;` ...) [1] https://github.com/Basile-z/iz/blob/9ce6fc0e2e0c74f97d530ce598a6842b7b048f25/import/iz/enumset.d#L1086
Apr 11 2019
On Thursday, 11 April 2019 at 07:56:42 UTC, Julian wrote:On Thursday, 11 April 2019 at 06:45:23 UTC, Basile B. wrote:Your enum is int so in machine code it's exactly like processing a machine word. It's not even worth benchmarking this ;) As a side note, and in case you would not know it yet, there's this traits that's useful with enums: https://dlang.org/phobos/std_traits.html#EnumMembersOn Thursday, 11 April 2019 at 06:20:05 UTC, Julian wrote:I don't see a difference in micro-benchmarks. *shrug*
Apr 11 2019