Jump to page: 1 2
Thread overview
How to make a struct containing an associative array to deeply copy (for repeated usage in foreach) ?
Mar 13
rkompass
Mar 14
monkyyy
Mar 14
rkompass
Mar 14
rkompass
Mar 14
Monkyyy
Mar 15
rkompass
Mar 15
monkyyy
Mar 15
rkompass
Mar 15
bachmeier
Mar 16
monkyyy
Mar 18
rkompass
Mar 18
rkompass
Mar 18
novice2
Mar 18
novice2
March 13

I want to make a custom dictionary that I may iterate through with foreach. Several times.
What I observe so far is that my dict as a simple forward range is exhausted after the first foreach and I have to deeply copy it beforehand.
With a simple associative array the exhaustion is not observed.
Is there a (hopefully simple) way to make this automatic/transparent? Of course
I need to use the struct.
Can I add a save member function? If yes: How? Or is there an operator that is used in the foreach initialization that I may overload in this struct?

My code:

import std.stdio;
import std.string;
import std.typecons;

struct mydict {
   string[string] dct;

   @property bool empty() const {
      return dct.empty;
   }
   @property ref auto front() {
      return tuple(dct.keys[0], dct[dct.keys[0]]);
   }
	void popFront() {
		dct.remove(dct.keys[0]);
	}
	void opAssign(mydict rhs) {
		writeln("--opAssign--");
		foreach (k; rhs.dct.keys)  // do a deep copy
			dct[k] = rhs.dct[k];
	}
}

void main() {

	mydict md, md2;
	md.dct = ["h":"no", "d":"ex", "r": "cow"];
	md2 = md;             // md2.opAssign(md)
	foreach (k, v; md)
		writeln("key: ", k, "val: ", v);
	writeln("----------");
	foreach (k, v; md)   // does not work with md again, md is exhausted
		writeln("key: ", k, "val: ", v);
}
March 14

On Wednesday, 13 March 2024 at 22:16:52 UTC, rkompass wrote:

>

I want to make a custom dictionary that I may iterate through with foreach. Several times.
What I observe so far is that my dict as a simple forward range is exhausted after the first foreach and I have to deeply copy it beforehand.
With a simple associative array the exhaustion is not observed.
Is there a (hopefully simple) way to make this automatic/transparent? Of course
I need to use the struct.
Can I add a save member function? If yes: How? Or is there an operator that is used in the foreach initialization that I may overload in this struct?

you need to seperate the range state from the data

import std;
auto tuple(T...)(T t){
    struct tuple{
        T me; alias me this;
    }
    return tuple(t);
}
struct mydict(T,S){
	T[S] dct;
	void opAssign(mydict rhs) {
		writeln("--opAssign--");
		foreach (k; rhs.dct.keys)  // do a deep copy
			dct[k] = rhs.dct[k];
	}
    auto opSlice(){
        struct range{
            T[S]* parent;
            int i;
            auto front()=> tuple(parent.keys[i],(*parent)[parent.keys[i]]);
            auto popFront()=>i++;
            auto empty()=>parent.keys.length<=i;
        }
        return range(&this.dct);
    }
}

void main() {

	mydict!(string,string) md, md2;
	md.dct = ["h":"no", "d":"ex", "r": "cow"];
	md2 = md;             // md2.opAssign(md)
	foreach (k, v; md[])
		writeln("key: ", k, "val: ", v);
	writeln("----------");
	foreach (k, v; md[])   // does not work with md again, md is exhausted
		writeln("key: ", k, "val: ", v);
}
March 14

Hello @monkyyy,

thank you for your help. I will study and try your code.

Meanwhile I have found that I can add this function into the struct:

// postblit constructor, see
// https://stackoverflow.com/questions/38785624/d-struct-copy-constructor
	this(this) {
		string[string] ndct;
		foreach (k; dct.keys)  // do a deep copy
			ndct[k] = dct[k];
		dct = ndct;
	}
March 14

On Thursday, 14 March 2024 at 16:12:00 UTC, rkompass wrote:
Hello @monkyyy again,

your solution is much more elegant:-) No need to do a deep copy.
I was thinking about a way to achieve that but had no clue.

I will study opSlice now.

Your definition of tuple is somewhat obscure to me.
Is this compatible with the Tuple from std.typecons?

Greetings,
Raul

March 14

On Thursday, 14 March 2024 at 16:32:10 UTC, rkompass wrote:

>

On Thursday, 14 March 2024 at 16:12:00 UTC, rkompass wrote:
Hello @monkyyy again,

your solution is much more elegant:-) No need to do a deep copy.
I was thinking about a way to achieve that but had no clue.

I will study opSlice now.

Your definition of tuple is somewhat obscure to me.
Is this compatible with the Tuple from std.typecons?

Greetings,
Raul

Std.tuple is a gaint mess if there's differences in usage syntax they are very stupid tradeoffs; fundementally the base languge has allot of syntax sugar for "aliasSeq" and my definition of tuple is sticking an Seq into a struct; if you look at the code for std.tuple it will do the same thing under the name "expand"

If you go down the rabbit hole of learning the template syntax you should start with the systax aviable for seq's and 95% of it will apply to that definition of tuple

March 15

@Monkyyy: I adopted your solution, it is perfect.

I only have one problem left:

The foreach loop with associative arrays has two cases:

foreach(key, val; arr) and foreach(x; arr).
In the second case only the values are iterated.
With the present solution the iteration delivers (key, val) tuples.

Can this somehow be detected by the opSlice or is there another overloading
construct to be applied for this?

Addition: I noted that in the best matching docs only ordinary arrays are covered. Your solution would make a very nice addition for the case of associative arrays there. I learn't a lot from it.
Should I do an improvement request somewhere?

March 15

On Friday, 15 March 2024 at 09:03:25 UTC, rkompass wrote:

>

@Monkyyy: I adopted your solution, it is perfect.

I only have one problem left:

The foreach loop with associative arrays has two cases:

foreach(key, val; arr) and foreach(x; arr).
In the second case only the values are iterated.
With the present solution the iteration delivers (key, val) tuples.

That will not be fixed in d2 ranges and has no good solutions; and my affect over d3 seems to be none. You could ask around for the "opApply" solution but I dont know it well (and prefer ranges)

d2 Ranges are based on a simplification of stl's ideas and stl doesn't support arrays-like iteration well, I wish to change that and working on a proof of concept algorthims lib... but well, this is unlikely to work.

For d3 if changing the range interface fails, expect to see style guides say "prefer explict range starters" string.byUnicode and string.byAscii will probably be how they kill autodecoding and your data stucture having 2 range functions as byKey and byKeyValue will look the same.

>

Should I do an improvement request somewhere?

I think its been kinda of piecemeal and D1 1D(repetition intentional) opSlice is in limbo(it was deprecated, and then slightly undepercated in some random chats, its a mess)

for completeness I believe the current state of 1d op overloads are:

opIndex(int)
opIndex(key)
opSlice()
opSlice(int, int)
int opDollar()
dollar opDollar()
opSlice(int, dollar)
opBinararyRight("in",K)(key) (opIn was deprecated and shouldn't have been)

If your confident in your writing ability id suggest a clean slate article based on this list and what the compiler actually does(maybe ask around for any I missed) rather than trying to untangle this mess

Or write a dip thread "undeperacate d1 opOverloads that are still wanted by everyone") and try to bring back opIn at the same time and get the limboness of old technically deprecated 1d array opOverloads officially gone

March 15

On Friday, 15 March 2024 at 17:15:56 UTC, monkyyy wrote:

>

On Friday, 15 March 2024 at 09:03:25 UTC, rkompass wrote:

>

@Monkyyy: I adopted your solution, it is perfect.

I only have one problem left:

The foreach loop with associative arrays has two cases:

foreach(key, val; arr) and foreach(x; arr).
In the second case only the values are iterated.
With the present solution the iteration delivers (key, val) tuples.

That will not be fixed in d2 ranges and has no good solutions; and my affect over d3 seems to be none. You could ask around for the "opApply" solution but I dont know it well (and prefer ranges)

d2 Ranges are based on a simplification of stl's ideas and stl doesn't support arrays-like iteration well, I wish to change that and working on a proof of concept algorthims lib... but well, this is unlikely to work.

For d3 if changing the range interface fails, expect to see style guides say "prefer explict range starters" string.byUnicode and string.byAscii will probably be how they kill autodecoding and your data stucture having 2 range functions as byKey and byKeyValue will look the same.

>

Should I do an improvement request somewhere?

I think its been kinda of piecemeal and D1 1D(repetition intentional) opSlice is in limbo(it was deprecated, and then slightly undepercated in some random chats, its a mess)

for completeness I believe the current state of 1d op overloads are:

opIndex(int)
opIndex(key)
opSlice()
opSlice(int, int)
int opDollar()
dollar opDollar()
opSlice(int, dollar)
opBinararyRight("in",K)(key) (opIn was deprecated and shouldn't have been)

If your confident in your writing ability id suggest a clean slate article based on this list and what the compiler actually does(maybe ask around for any I missed) rather than trying to untangle this mess

Or write a dip thread "undeperacate d1 opOverloads that are still wanted by everyone") and try to bring back opIn at the same time and get the limboness of old technically deprecated 1d array opOverloads officially gone

I'm quite new to D yet. But I have some acquaintance with Python.
Therefore, together with templates the discovery of the Variant type was inspiring me to the following:
I wanted to explore if it's possible to do sort of type-agnostic programming with D. This could perhaps enable a simpler translation of Python code to D.

Trying with a Variant[Variant] dct; dictionary I observed that even simple assignment of key:value pairs was not possible as the different types are not automatically cast to a Variant.

Embedded in a struct with templating and casts to Variant such a dict now seems possible:

The preliminary code:

// implement .get .update .require

import std.stdio;
import std.typecons;
import std.range;
import std.variant;
import std.string;
import std.format;

struct dict
{
	Variant[Variant] dct;

    Variant opIndex(T)(T key) {
		return dct[cast(Variant) key];
	}
    void opIndexAssign(V, T)(V val, T key) {
		dct[cast(Variant) key] = cast(Variant) val;
	}
	auto opBinaryRight(string op : "in", T)(T lhs) {
		return cast(Variant)lhs in dct;
	}
	@property auto keys() {
		return dct.keys;
	}
	@property auto values() {
		return dct.values;
	}
	auto remove(T)(T key) {
		return dct.remove(cast(Variant) key);
	}
	@property auto dup() {
		dict newd;
		foreach (k; dct.keys)  // do a deep copy
			newd.dct[k] = dct[k];
		return newd;
	}
	void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) {
		put(sink, "dict([");
		bool rep = false;
		foreach (k; dct.keys) {
			if (rep)
				put(sink, ", ");
			formatValue(sink, k, fmt);
			put(sink, ":");
			formatValue(sink, dct[k], fmt);
			rep = true;
		}
		put(sink, "])");
	}
	auto opSlice(){
		struct range{
			Variant[Variant]* parent;
			int i;
			auto front()=> tuple(parent.keys[i],(*parent)[parent.keys[i]]);
			auto popFront()=>i++;
			auto empty()=>parent.keys.length<=i;
		}
		return range(&this.dct);
	}
}

void main() {

	dict d;

	writeln("d: ", d);            // ==> dict([])
	writeln("d.keys: ", d.keys);
	writeln("d.values: ", d.values);
	writeln("d.keys.length: ", d.keys.length);
	writeln("------------");
	
	writeln("populating dict ....");
	d["hello"] = 2;
	d[3.1] = 5;
	d['c'] = 3.14;
	d[2] = "freak";
	d["mykey"] = 42;

	writeln("------------");
	
	writeln("d: ", d);            // ==> dict([2:0, mykey:42, 3.1:5, c:1, hello:2])
	writeln("d.keys: ", d.keys);
	writeln("d.values: ", d.values);
	writeln("d.keys.length: ", d.keys.length);
	writeln("------------");

	writeln(d["hello"], ", ", d[3.1], ", ", d['c'], ", ", d[2], ", ", d["mykey"]);
	writeln("------------");
	
	d.remove("hello");
	writeln("\"hello\" in d:", "hello" in d);
	writeln("3.1 in d:", 3.1 in d);
	writeln("------------");
	foreach (key, value; d)                 // does not exhaust d
		writeln("key: ", key, "  value: ", value);
	writeln("------------");
	foreach (key, value; d)
		writeln("key: ", key, "  value: ", value);
	writeln("------------");
	auto d2 = d.dup;
	foreach (kv; d2)
		writeln("kv: ", kv);
}

Probably this is useless, but it was a nice challenge for learning.

>

d2 Ranges are based on a simplification of stl's ideas and stl doesn't support
arrays-like iteration well, I wish to change that and working on a proof of concept algorthims lib... but well, this is unlikely to work.

>

For d3 if changing the range interface fails, expect to see style guides say "prefer explict range starters" string.byUnicode and string.byAscii will probably be how they kill autodecoding and your data stucture having 2 range functions as byKey and byKeyValue will look the same.

I start to see that D is heavily influenced by C++ (STL), not just C.
This is not bad but might it make more difficult for people coming from other directions like Python to get acquainted with it.
Given that there are alternative developments like e.g. Mojo attempting to make Python to a transparently compiled language with C-like speed.

I was annoyed by C++'s complicated style from the beginning and am very happy now to have discovered D. Likewise it was with Markup/HTML and now I'm happy to see Markdown. Discussing with friends about this lead to: The complications (C++) were necessary perhaps to find a simple solution (D) in the end.
I cannot judge the range and similar concepts D now (not enough knowledge), but hope that the best combination of simplicity/clarity/might will prevail in future development.

March 15

On Friday, 15 March 2024 at 20:36:56 UTC, rkompass wrote:

>

I'm quite new to D yet. But I have some acquaintance with Python.
Therefore, together with templates the discovery of the Variant type was inspiring me to the following:
I wanted to explore if it's possible to do sort of type-agnostic programming with D. This could perhaps enable a simpler translation of Python code to D.

Trying with a Variant[Variant] dct; dictionary I observed that even simple assignment of key:value pairs was not possible as the different types are not automatically cast to a Variant.

You're not the first one. There's no technical reason for the restriction. It's simply a matter of being opposed by those who make these decisions on the basis that it's the wrong way to program or something like that. Here is a recent thread: https://forum.dlang.org/post/ikwphfwevgnsxmdfqqkl@forum.dlang.org

March 16

On Friday, 15 March 2024 at 20:36:56 UTC, rkompass wrote:

>

I start to see that D is heavily influenced by C++ (STL), not just C.
This is not bad

It is just bad; ranges are not pairs of 2 pointers, stepov was comprising with c++ or if he thinks c++ iterators are objectively good(not good for making something work with c++) he's lacks taste

even simplified a "random access range" is to complex an api(close to 20 functions) when Im pretty sure you need 6 functions for a coherent array-like interface and when your talking about 100 algorthims, well 1400 functions matters

« First   ‹ Prev
1 2