www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is it possible to add items to the arrays and hashes at compile time?

reply "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
Does D the ability to add items to arrays and hashes at compile 
time?

For example, how do I do it in compile time?:

int[][int][int] hash;

hash[4][6] ~= [34, 65];
hash[5][7] ~= [4, 78, 21];
Jun 07 2015
parent reply "Nicholas Wilson" <iamthewilsonator hotmail.com> writes:
On Sunday, 7 June 2015 at 12:30:12 UTC, Dennis Ritchie wrote:
 Does D the ability to add items to arrays and hashes at compile 
 time?

 For example, how do I do it in compile time?:

 int[][int][int] hash;

 hash[4][6] ~= [34, 65];
 hash[5][7] ~= [4, 78, 21];
try using a pure function + static e.g. int[][int][int] somePureDefaultHash() pure { ... //initialise it here } ... static hash = somePureDefaultHash();
Jun 07 2015
next sibling parent reply "Nicholas Wilson" <iamthewilsonator hotmail.com> writes:
On Sunday, 7 June 2015 at 12:42:12 UTC, Nicholas Wilson wrote:
 On Sunday, 7 June 2015 at 12:30:12 UTC, Dennis Ritchie wrote:
 Does D the ability to add items to arrays and hashes at 
 compile time?

 For example, how do I do it in compile time?:

 int[][int][int] hash;

 hash[4][6] ~= [34, 65];
 hash[5][7] ~= [4, 78, 21];
try using a pure function + static e.g. int[][int][int] somePureDefaultHash() pure { ... //initialise it here } ... static hash = somePureDefaultHash();
or enum
Jun 07 2015
next sibling parent "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Sun, Jun 07, 2015 at 12:43:16PM +0000, Nicholas Wilson via
Digitalmars-d-learn wrote:
 On Sunday, 7 June 2015 at 12:42:12 UTC, Nicholas Wilson wrote:
On Sunday, 7 June 2015 at 12:30:12 UTC, Dennis Ritchie wrote:
Does D the ability to add items to arrays and hashes at compile
time?

For example, how do I do it in compile time?:

int[][int][int] hash;

hash[4][6] ~= [34, 65];
hash[5][7] ~= [4, 78, 21];
try using a pure function + static e.g. int[][int][int] somePureDefaultHash() pure { ... //initialise it here } ... static hash = somePureDefaultHash();
or enum
Using an enum for an array or hash is usually a bad idea, because it causes a runtime allocation per reference to that symbol. T -- "I suspect the best way to deal with procrastination is to put off the procrastination itself until later. I've been meaning to try this, but haven't gotten around to it yet. " -- swr
Jun 07 2015
prev sibling parent "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
On Sunday, 7 June 2015 at 12:43:17 UTC, Nicholas Wilson wrote:
 or enum
I suspect that enum can not add elements to the array.
Jun 07 2015
prev sibling parent reply "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
On Sunday, 7 June 2015 at 12:42:12 UTC, Nicholas Wilson wrote:
 On Sunday, 7 June 2015 at 12:30:12 UTC, Dennis Ritchie wrote:
 try using a pure function + static e.g.

  int[][int][int] somePureDefaultHash() pure
 {
     ... //initialise it here
 }

 ...
 static hash = somePureDefaultHash();
static int[][int][int] hash; hash[4][6] ~= [34, 65]; static if (!!(4 in hash)) {} // Error: static variable hash cannot be read at compile time
Jun 07 2015
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 06/07/2015 05:56 AM, Dennis Ritchie wrote:
 On Sunday, 7 June 2015 at 12:42:12 UTC, Nicholas Wilson wrote:
 On Sunday, 7 June 2015 at 12:30:12 UTC, Dennis Ritchie wrote:
 try using a pure function + static e.g.

  int[][int][int] somePureDefaultHash() pure
 {
     ... //initialise it here
 }

 ...
 static hash = somePureDefaultHash();
static int[][int][int] hash; hash[4][6] ~= [34, 65]; static if (!!(4 in hash)) {} // Error: static variable hash cannot be read at compile time
/* Some function that generates an AA */ int[][int][int] initHash(int i) { /* It is nice to see that this function is not called at run time */ if (!__ctfe) { import std.stdio; writefln("%s is called at run time", __FUNCTION__); } return [i : [ i : [i, i] ] ]; } /* Question: Is there a function to merge two AAs? */ int[][int][int] merge()(int[][int][int][] hashes...) { /* It is nice to see that this function is not called at run time */ if (!__ctfe) { import std.stdio; writefln("%s is called at run time", __FUNCTION__); } int[][int][int] result; foreach (hash; hashes) { foreach (key, value; hash) { result[key] = value; } } return result; } /* These three are generated at compile time */ enum firstPart = initHash(1); enum secondPart = initHash(2); enum int[][int][int] ctHash = merge(firstPart, secondPart); /* Although ctHash is useful by itself, as H. S. Teoh said, every * reference to that AA at run time will generate a new AA. So, we * better refer to it just once. * * Luckily, as the __ctfe expressions above prove, the initialization * happens only once at compile time. I guess the memory layout of * 'ctHash' is simply copied to 'hash' below. */ int[][int][int] hash; static this() { hash = ctHash; } void main() { import std.stdio; writeln(hash); /* This is to see that the slice elements are not generated at * every reference. (It is good that .ptr value of each member * slice is the same.) */ writeln(hash[1][1].ptr, ' ', hash[2][2].ptr); writeln(hash[1][1].ptr, ' ', hash[2][2].ptr); } Ali
Jun 07 2015
parent reply "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
On Sunday, 7 June 2015 at 15:20:17 UTC, Ali Çehreli wrote:
 /* Some function that generates an AA */
Thanks. Beautiful code, but I want a little more :) /* Some function that generates an AA */ int[][int][int] initHash(int i) { /* It is nice to see that this function is not called at run time */ if (!__ctfe) { import std.stdio; writefln("%s is called at run time", __FUNCTION__); } return [i : [ i : [i, i] ] ]; } /* Question: Is there a function to merge two AAs? */ int[][int][int] merge(Hash)(Hash[] hashes...) { /* It is nice to see that this function is not called at run time */ if (!__ctfe) { import std.stdio; writefln("%s is called at run time", __FUNCTION__); } int[][int][int] result; foreach (hash; hashes) { foreach (key, value; hash) { result[key] = value; } } return result; } /* These three are generated at compile time */ enum firstPart = initHash(1); enum secondPart = initHash(2); enum int[][int][int] ctHash = merge(firstPart, secondPart); void main() { import std.stdio; static if (!(4 in ctHash)) {{ // ... }} ctHash[4][4] ~= [4, 4]; // I want this to work at compile time :) // Possible? static if (!!(4 in ctHash)) {{ // ... }} }
Jun 09 2015
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 06/09/2015 06:53 PM, Dennis Ritchie wrote:

      ctHash[4][4] ~= [4, 4]; // I want this to work at compile time :)
      // Possible?
It is possible but not exactly as you wrote. In other words, it is not as flexible. The way I understand it and the way it makes sense to me, :) variables that are generated at compile time can be initialized only once. It is not possible after initialization. However, the initialization of the variable can be as complex as needed: enum int[][int][int] ctHash = init_ctHash(); int[][int][int] init_ctHash(){ auto result = merge(firstPart, secondPart); result[4] = [4 : [4, 4 ]]; return result; } (I couldn't get ctHash[4][4] to work; so I changed the code like above.) Ali
Jun 09 2015
parent reply "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
On Wednesday, 10 June 2015 at 03:38:32 UTC, Ali Çehreli wrote:
 The way I understand it and the way it makes sense to me, :) 
 variables that are generated at compile time can be initialized 
 only once. It is not possible after initialization. However, 
 the initialization of the variable can be as complex as needed:
Thanks. It turns out I can do this: import std.stdio; auto merge(Hashes)(Hashes[] hashes...) { int[][int][int] result; foreach (hash; hashes) { foreach (key, value; hash) { result[key] = value; } } return result; } enum firstPart = [1 : [ 1 : [1, 1] ] ]; enum secondPart = [2 : [ 2 : [2, 2] ] ]; int[][int][int] init_ctHash(int i) { auto result = merge(firstPart, secondPart); result[i] = [ i : [i, i] ]; return result; } void main() { enum int[][int][int] ctHash = init_ctHash(5); enum t = merge(ctHash, init_ctHash(6)); writeln(t); } But I can not do so: enum int[][int][int] ctHash = init_ctHash(5); ctHash = merge(ctHash, init_ctHash(6)); I have a question: why variables may not be initialized more than once? Why can't they to resave at compile time?
Jun 09 2015
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 06/09/2015 09:36 PM, Dennis Ritchie wrote:

 But I can not do so:

 enum int[][int][int] ctHash = init_ctHash(5);

 ctHash = merge(ctHash, init_ctHash(6));

 I have a question: why variables may not be initialized more than once?
 Why can't they to resave at compile time?
My phrasing was off: By definition, initialization happens once. :) What I meant is, once initialized, a compile-time variable cannot be reassigned. The reason is, to effect compile time evaluation, one needs to use 'enum' (or 'static const') but 'enum' is a literal, i.e. it cannot be modified. As I've shown, it is possible to use an expression that will be used as the value of the compile-time variable. As long as it is evaluable at compile time, the expression can be arbitrarily complex. Ali
Jun 10 2015
parent reply "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
On Wednesday, 10 June 2015 at 07:15:26 UTC, Ali Çehreli wrote:
 My phrasing was off: By definition, initialization happens 
 once. :) What I meant is, once initialized, a compile-time 
 variable cannot be reassigned. The reason is, to effect compile 
 time evaluation, one needs to use 'enum' (or 'static const') 
 but 'enum' is a literal, i.e. it cannot be modified.

 As I've shown, it is possible to use an expression that will be 
 used as the value of the compile-time variable. As long as it 
 is evaluable at compile time, the expression can be arbitrarily 
 complex.
I understand your phrase :) I don't understand why the variables at compile time cannot be reassigned. I.e. why can't we use `int` instead of `enum` or `immutable`? Isnt it possible to come up with the interpreter compile-time, which will determine the operating time of the program at runtime at compile time. And if this time is small, it is possible to reassign variables at compile time more than once. Maybe it's something out of science fiction, but still. Is it possible somehow to create a more complex compilation process, which can reassign variables more than once?
Jun 10 2015
next sibling parent reply "anonymous" <anonymous example.com> writes:
On Wednesday, 10 June 2015 at 17:00:34 UTC, Dennis Ritchie wrote:
 Isnt it possible to come up with the interpreter compile-time, 
 which will determine the operating time of the program at 
 runtime at compile time.
Sounds like the halting problem. So, no, generally this is not possible.
Jun 10 2015
parent "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
On Wednesday, 10 June 2015 at 17:13:34 UTC, anonymous wrote:
 On Wednesday, 10 June 2015 at 17:00:34 UTC, Dennis Ritchie 
 wrote:
 Isnt it possible to come up with the interpreter compile-time, 
 which will determine the operating time of the program at 
 runtime at compile time.
Sounds like the halting problem. So, no, generally this is not possible.
Thanks. I had never heard of the halting problem. This is exactly what I wanted to know.
Jun 10 2015
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 06/10/2015 10:00 AM, Dennis Ritchie wrote:

 Is it possible somehow
 to create a more complex compilation process, which can reassign
 variables more than once?
I am not a compiler writer but I assume if a variable is not a compile-time expression, then the compiler generates code that makes it possible to initialize it at run time and modify it if mutable: int i = argc; // ... ++i; On the other hand, if it's a manifest constant (enum, const static, etc.) then by definition it cannot be mutated. If we allowed mutation of compile-time expressions, then we would have a complicated language. enum i = 42; enum j = foo(i); // Did foo() use 42 or 43? i = 43; enum k = foo(i); // Did foo() use 42 or 43? How can an enum value be changed? I find the above confusing. The great thing about D's CTFE is that we can use arbitrarily complex expressions as long as they are available at compile time. For example, it is possible to make 'i' above a foreach loop variable and call foo() with different values. What is your use case? I feel like it can be solved by other means. Ali
Jun 10 2015
parent "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
On Wednesday, 10 June 2015 at 17:43:36 UTC, Ali Çehreli wrote:
 On the other hand, if it's a manifest constant (enum, const 
 static, etc.) then by definition it cannot be mutated. If we 
 allowed mutation of compile-time expressions, then we would 
 have a complicated language.
Unfortunately, the halting problem says that the analyzer does not exist. Although I don't believe it!
 enum i = 42;
 enum j = foo(i);    // Did foo() use 42 or 43?
 i = 43;
 enum k = foo(i);    // Did foo() use 42 or 43?

 How can an enum value be changed? I find the above confusing.
So in fact, if `int` is evaluated at compile time, then we won't need constants or enums. Because they almost would not make sense. Although the issues associated with immutability, complex, I think they can be solved differently.
 The great thing about D's CTFE is that we can use arbitrarily 
 complex expressions as long as they are available at compile 
 time. For example, it is possible to make 'i' above a foreach 
 loop variable and call foo() with different values.
There is nothing great. Everything is based on constexpr of C++. I want something more revolutionary :)
 What is your use case? I feel like it can be solved by other 
 means.
There is no precedent special :) I just want to know more about the possibilities of D and the possibility of its compiler. D made me a terrible interest in compilers :)
Jun 10 2015