Efficient enum array keys?
Julian
julian.fondren at gmail.com
Thu Apr 11 06:20:05 UTC 2019
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 Rune is ('#', 'G', 'E', '.');
type Runestring is array (1 .. N) of Rune;
Maze : array (1 .. N) of Runestring :=
("################################",
"#######..#######..#.G..##..#####",
"######.....#####.....GG.##.#####",
... 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?
More information about the Digitalmars-d-learn
mailing list