On 8/1/20 7:00 PM, Andy Balba wrote:

>> >> ubyte[3][4] c = [ [5, 5, 5], [15, 15,15], [25, 25,25], [35,
>> 35,35]  ];

> Although not detailed in my original question, in my actual app
> I have array ubyte [1000][3] Big which consists of research data I
> obtained,
>   and from which I want to randomly select 4 observations to construct
> ubyte c[ ][ ].

It depends on how the data is layed out. Your original question makes me think the data is "code generated" into a D module and that's where you want to initialize that 2D fixed-length (static) array.

So, you simply want to import that module in your analysis program:

import data;

However, I really think the type should be ubyte[3][].

The following option reads the data from a file before analysis. Assuming the data is formatted one ubyte on a line:

--- 8< ---
5
5
5
15
15
15
25
25
25
35
35
35
--- 8< ---

then the following program does what you want:

import std.stdio : File, writefln;
import std.algorithm : map;
import std.range : array, chunks;
import std.string : strip;
import std.conv : to;
import std.random : choice;

auto parseData(string fileName) {
  return
    File(fileName)       // - Open the file
    .byLine              // - Iterate line-by-line as a range
                         //   (WARNING: same line buffer is
                         //   shared use byLineCopy if
                         //   necessary.)
    .map!strip           // - Strip the element
    .map!(to!ubyte)      // - Convert to ubyte
    .chunks(3)           // - Treat 3 consecutive elements as one
                         //   unit
    .map!(c => c.array)  // - Make an array from each chunk
    ;

  // NOTE: The returned range object is a chain of
  // operations to apply on fileName. Nothing will be read
  // from the file until the returned range is actually
  // used (i.e. the range is "lazy").
}

void main() {
  auto c = parseData("research_data")  // - The lazy range
           .array;                     // - As we want to pick
                                       //   random elements; we
                                       //   convert the data
                                       //   range to an
                                       //   array. This step is
                                       //   "eager": the entire
                                       //   file is parsed here.

  // Demonstrate that the type is a 2D array
  static assert (is (typeof(c) == ubyte[][]));

  // The rest is a random choice from that array:
  foreach (_; 0 .. 10) {
    auto chosen = c.choice;
    writefln!"chosen: %s"(chosen);
  }
}

If the three ubytes should actually be used as parts of a struct like Color, here is another approach:

import std.stdio;
import std.algorithm;
import std.range;
import std.string;
import std.conv;
import std.random;

struct Color {
  ubyte r;
  ubyte g;
  ubyte b;
}

// Assumes data is one ubyte per line
auto parseData(string fileName) {
  return
    File(fileName)
    .byLine
    .map!strip
    .map!(to!ubyte);
}

auto makeColors(R)(R range) {
  Color[] colors;

  while (!range.empty) {
    ubyte pop() {
      auto value = range.front;
      range.popFront();
      return value;
    }

    auto r = pop();
    auto g = pop();
    auto b = pop();

    colors ~= Color(r, g, b);
  }

  return colors;
}

void main() {
  auto data = parseData("research_data");

  auto c = makeColors(data);
  writefln!"data:\n%(%s\n%)"(c);

  foreach (_; 0 .. 10) {
    auto chosen = c.choice;
    writefln!"chosen: %s"(chosen);
  }
}

Sorry my liberal use of 'auto', which hides types. You can define 'c' with explicit types like Color[], or you can expose a variable's type at compile time with pragma(msg):

  pragma(msg, "the type: ", typeof(c));

That prints Color[] for the second program above.

Ali

Reply via email to