www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Defining a static array with values in a range

reply "tcak" <tcak gmail.com> writes:
I want to define alphanumeric characters in an easy way. 
Something like that:

char[] arr = ['a'..'z', 'A'..'Z', '0'..'9'];

Though above example doesn't work. Is there any easy way to do 
this?

I am trying to do something like EBNF definitions. So, I do not 
want to use loops, or define every single thing one by one by 
hand.
Jan 21 2015
next sibling parent "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
On Thursday, 22 January 2015 at 05:56:40 UTC, tcak wrote:
 I want to define alphanumeric characters in an easy way. 
 Something like that:

 char[] arr = ['a'..'z', 'A'..'Z', '0'..'9'];

 Though above example doesn't work. Is there any easy way to do 
 this?

 I am trying to do something like EBNF definitions. So, I do not 
 want to use loops, or define every single thing one by one by 
 hand.
This is interesting, I think this syntax would be pretty cool if it did work (I think it would work in foreach loops... at least it does with integers). Anyway, what you could do is define a static struct with slicing overloaded for chars, and have it return slices of a master char array or a generating function. Assume you called the struct C, then your char array declaration could looks something like join (C['a'..'z'], C['A'..'Z'], C['0'..'9']) It is a bit of up-front work, though it is maintainable. I'm also curious if there is indeed an easier way.
Jan 21 2015
prev sibling next sibling parent reply "anony" <anonymous mail.invalid> writes:
On Thursday, 22 January 2015 at 05:56:40 UTC, tcak wrote:
 I want to define alphanumeric characters in an easy way. 
 Something like that:

 char[] arr = ['a'..'z', 'A'..'Z', '0'..'9'];

 Though above example doesn't work. Is there any easy way to do 
 this?

 I am trying to do something like EBNF definitions. So, I do not 
 want to use loops, or define every single thing one by one by 
 hand.
There are convenient constants defined in std.ascii. import std.ascii; string arr = lowercase ~ uppercase ~ digits; // also 'std.ascii.letters' gives ('A' .. 'Z' ~ 'a' .. 'z')
Jan 21 2015
parent reply "tcak" <tcak gmail.com> writes:
On Thursday, 22 January 2015 at 07:29:05 UTC, anony wrote:
 On Thursday, 22 January 2015 at 05:56:40 UTC, tcak wrote:
 I want to define alphanumeric characters in an easy way. 
 Something like that:

 char[] arr = ['a'..'z', 'A'..'Z', '0'..'9'];

 Though above example doesn't work. Is there any easy way to do 
 this?

 I am trying to do something like EBNF definitions. So, I do 
 not want to use loops, or define every single thing one by one 
 by hand.
There are convenient constants defined in std.ascii. import std.ascii; string arr = lowercase ~ uppercase ~ digits; // also 'std.ascii.letters' gives ('A' .. 'Z' ~ 'a' .. 'z')
Well, that's just disguising what we can't do. D has alot of compile time structures, even much complex than what I asked. So, this type of thing should be doable for immutable arrays.
Jan 22 2015
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
tcak:
 Well, that's just disguising what we can't do.
When the a..b syntax was added to foreach() someone criticized that syntax saing it's a "one trick pony", and indeed I don't know why Walter didn't make it a little more first-class. But note that in D the a..b ranges are always open on the right, so 'a'..'z' can't include the 'z'. What do you think of this? import std.stdio, std.range, std.algorithm, std.array; auto charInterval(in char a, in char b) pure nothrow safe nogc { return iota(a, b + 1).map!(i => cast(char)i); } void main() safe { char[] arr = chain(charInterval('a', 'z'), charInterval('A', 'Z'), charInterval('0', '9')).array; arr.writeln; } charInterval can of course become eager too, so you just need ~ to concatenate the results: import std.stdio, std.range, std.algorithm, std.array; char[] charInterval(in char a, in char b) pure nothrow safe { return iota(a, b + 1).map!(i => cast(char)i).array; } void main() safe { char[] arr = charInterval('a', 'z') ~ charInterval('A', 'Z') ~ charInterval('0', '9'); arr.writeln; } Bye, bearophile
Jan 22 2015
prev sibling parent "Kagamin" <spam here.lot> writes:
On Thursday, 22 January 2015 at 09:26:21 UTC, tcak wrote:
 There are convenient constants defined in std.ascii.

 import std.ascii;
 string arr = lowercase ~ uppercase ~ digits;

 // also 'std.ascii.letters' gives ('A' .. 'Z' ~ 'a' .. 'z')
Well, that's just disguising what we can't do. D has alot of compile time structures, even much complex than what I asked. So, this type of thing should be doable for immutable arrays.
If you declare the string as immutable, the concatenation will be done at compile time.
Jan 22 2015
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Thursday, January 22, 2015 05:56:39 tcak via Digitalmars-d-learn wrote:
 I want to define alphanumeric characters in an easy way.
 Something like that:

 char[] arr = ['a'..'z', 'A'..'Z', '0'..'9'];

 Though above example doesn't work. Is there any easy way to do
 this?

 I am trying to do something like EBNF definitions. So, I do not
 want to use loops, or define every single thing one by one by
 hand.
std.range.iota is what's used to creat ranges of values, and std.array.array can be used to convert a range to an array. So, the functionality si in the standard library, even if it's not quite as clean as you might like it to be. e.g. import std.range; alias uiota = iota!(ubyte, ubyte); auto r = chain(uiota('a', 'z'), uiota('A', 'Z'), uiota('0', '9')); auto arr = cast(char[])array(r); And actually, it's worse with chars than it would be with integers, because for some reason, iota doesn't seem to want to operate directly on char, but presumably that could be fixed, which would help clean up the code. But regardless, the functionality is there without needing to add it to the language, much as it might be nice to have it in the language where it would look a bit cleaner. - Jonathan M Davis
Jan 22 2015
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 auto r = chain(uiota('a', 'z'), uiota('A', 'Z'), uiota('0', 
 '9'));
Those ranges are probably open on the right. In Bugzilla I have asked for the syntax iota!"[]"(a, b) to change how the extrema are handled, modelled on std.random.uniform syntax. --------------- Kagamin:
 If you declare the string as immutable, the concatenation will 
 be done at compile time.
In function-scope I think you need enum. Bye, bearophile
Jan 22 2015
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Thursday, January 22, 2015 10:42:59 bearophile via Digitalmars-d-learn wrote:
 Jonathan M Davis:

 auto r = chain(uiota('a', 'z'), uiota('A', 'Z'), uiota('0',
 '9'));
Those ranges are probably open on the right.
They probably are actually, since open on the right is usually how things are done, but that's easy fixed with some +1's.
 Kagamin:

 If you declare the string as immutable, the concatenation will
 be done at compile time.
In function-scope I think you need enum.
Yeah. immutable has nothing to do with compile time, though if you're talking about concatenating constants, there's a decent chance that they'd be optimized so that no concatenation occurs at runtime. However, to force anything to happen at compile time, you need to be initializing something that _has_ to be initialized at compile time (enum, static variable, direct initialization of member variable, etc.). If the compiler ever has a choice, it won't do it aside from built-in stuff that it understands well enough to translate as an optimization. - Jonathan M Davis
Jan 22 2015
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 but that's easy fixed with some +1's.
But the +1 changes the char to an int. Bye, bearophile
Jan 22 2015
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Thursday, January 22, 2015 15:16:07 bearophile via Digitalmars-d-learn wrote:
 Jonathan M Davis:

 but that's easy fixed with some +1's.
But the +1 changes the char to an int.
True, though iota doesn't seem to like to operate on char anyway, so from the little playing around with it I did to answer the OP, it looks like you're forced to use casts for it anyway. And depending, you probably want to cast to ubyte and then convert that to char when you convert the range to an array to avoid the conversion to dchar anyway. So, all around, trying to use iota with char is a bit awkward. - Jonathan M Davis
Jan 22 2015
parent reply "tcak" <tcak gmail.com> writes:
On Thursday, 22 January 2015 at 17:15:34 UTC, Jonathan M Davis 
via Digitalmars-d-learn wrote:
 On Thursday, January 22, 2015 15:16:07 bearophile via 
 Digitalmars-d-learn wrote:
 Jonathan M Davis:

 but that's easy fixed with some +1's.
But the +1 changes the char to an int.
True, though iota doesn't seem to like to operate on char anyway, so from the little playing around with it I did to answer the OP, it looks like you're forced to use casts for it anyway. And depending, you probably want to cast to ubyte and then convert that to char when you convert the range to an array to avoid the conversion to dchar anyway. So, all around, trying to use iota with char is a bit awkward. - Jonathan M Davis
So, at the end of the day (I left working on my Matcher class in the morning waiting an answer for this question), there is nothing to convert ['a'..'d', '0'..'3'] to ['a', 'b', 'c', 'd', '0', '1', '2', '3'] at compile time automatically.
Jan 22 2015
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
tcak:

 So, at the end of the day (I left working on my Matcher class 
 in the morning waiting an answer for this question), there is 
 nothing to convert ['a'..'d', '0'..'3'] to ['a', 'b', 'c', 'd', 
 '0', '1', '2', '3'] at compile time automatically.
Right. The 'a'..'d' is not first class, and you can't invent new syntax. Bye, bearophile
Jan 22 2015
prev sibling parent reply "Meta" <jared771 gmail.com> writes:
On Thursday, 22 January 2015 at 17:45:59 UTC, tcak wrote:
 So, at the end of the day (I left working on my Matcher class 
 in the morning waiting an answer for this question), there is 
 nothing to convert ['a'..'d', '0'..'3'] to ['a', 'b', 'c', 'd', 
 '0', '1', '2', '3'] at compile time automatically.
There is rarely never a way to do something in D, if you want to hack around a bit. import std.stdio; property charRange(string spec)() { import std.algorithm; import std.ascii; import std.conv; import std.range; import std.string; return spec.split(',').map!((s) { s = s.strip; auto start = s[1..$].front; auto end = s[0..$-1].back; return iota(start, end + 1).map!(c => cast(char)c).array; }).array.join; } void main(string[] argv) { auto t = charRange!q{ 'a'..'z', 'A'..'Z', '0'..'9' }; //abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 writeln(t); } This is very rough code, as it only works for ASCII codepoints, and it can't do backward intervals, but you get the idea.
Jan 22 2015
parent reply "Meta" <jared771 gmail.com> writes:
On Thursday, 22 January 2015 at 18:23:00 UTC, Meta wrote:
Whoops, I forgot to make it a template to force CTFE.

import std.stdio;

template charRange(string spec)
{
     static processInput(string spec)
     {
         import std.algorithm;
         import std.ascii;
         import std.conv;
         import std.range;
         import std.string;

         return spec.split(',').map!((s)
         {
             s = s.strip;
             auto start = s[1..$].front;
             auto end = s[0..$-1].back;

             return iota(start, end + 1).map!(c => 
cast(char)c).array;
         }).array.join;
     }

     enum charRange = processInput(spec);
}


void main(string[] argv)
{
     auto t = charRange!q{ 'a'..'z', 'A'..'Z', '0'..'9' };
     writeln(t);

     readln();
}
Jan 22 2015
parent reply zeljkog <zeljkog home.com> writes:
On 22.01.15 19:26, Meta wrote:
 On Thursday, 22 January 2015 at 18:23:00 UTC, Meta wrote:
 Whoops, I forgot to make it a template to force CTFE.
You can force CTFE assigning to manifest constant. enum t = charRange!...
Jan 22 2015
parent reply "Meta" <jared771 gmail.com> writes:
On Thursday, 22 January 2015 at 19:00:47 UTC, zeljkog wrote:
 On 22.01.15 19:26, Meta wrote:
 On Thursday, 22 January 2015 at 18:23:00 UTC, Meta wrote:
 Whoops, I forgot to make it a template to force CTFE.
You can force CTFE assigning to manifest constant. enum t = charRange!...
By wrapping it in a template the array will always be generated at compile time, even if you assign it to a runtime variable.
Jan 22 2015
parent reply zeljkog <zeljkog home.com> writes:
On 22.01.15 20:05, Meta wrote:
 On Thursday, 22 January 2015 at 19:00:47 UTC, zeljkog wrote:
 On 22.01.15 19:26, Meta wrote:
 On Thursday, 22 January 2015 at 18:23:00 UTC, Meta wrote:
 Whoops, I forgot to make it a template to force CTFE.
You can force CTFE assigning to manifest constant. enum t = charRange!...
By wrapping it in a template the array will always be generated at compile time, even if you assign it to a runtime variable.
Yes, but then you can not use runtime spec.
Jan 22 2015
parent reply "Meta" <jared771 gmail.com> writes:
On Thursday, 22 January 2015 at 19:12:32 UTC, zeljkog wrote:
 On 22.01.15 20:05, Meta wrote:
 On Thursday, 22 January 2015 at 19:00:47 UTC, zeljkog wrote:
 On 22.01.15 19:26, Meta wrote:
 On Thursday, 22 January 2015 at 18:23:00 UTC, Meta wrote:
 Whoops, I forgot to make it a template to force CTFE.
You can force CTFE assigning to manifest constant. enum t = charRange!...
By wrapping it in a template the array will always be generated at compile time, even if you assign it to a runtime variable.
Yes, but then you can not use runtime spec.
OP wanted it to be done at compile time.
Jan 22 2015
parent "Meta" <jared771 gmail.com> writes:
On Thursday, 22 January 2015 at 19:13:46 UTC, Meta wrote:
 On Thursday, 22 January 2015 at 19:12:32 UTC, zeljkog wrote:
 On 22.01.15 20:05, Meta wrote:
 On Thursday, 22 January 2015 at 19:00:47 UTC, zeljkog wrote:
 On 22.01.15 19:26, Meta wrote:
 On Thursday, 22 January 2015 at 18:23:00 UTC, Meta wrote:
 Whoops, I forgot to make it a template to force CTFE.
You can force CTFE assigning to manifest constant. enum t = charRange!...
By wrapping it in a template the array will always be generated at compile time, even if you assign it to a runtime variable.
Yes, but then you can not use runtime spec.
OP wanted it to be done at compile time.
A small improvement to ensure the input consists of all ASCII characters, and support for backward intervals. import std.stdio; template charRange(string spec) { static processSpec(string spec) { import std.algorithm; import std.ascii; import std.conv; import std.range; import std.string; return spec.split(',').map!((s) { s = s.strip; assert(s.all!(c => cast(ulong)c < 256), "Expected all characters in 'spec' to be ASCII"); auto start = cast(ubyte)s[1..$].front; auto end = cast(ubyte)s[0..$-1].back; return start <= end ? iota(start, end + 1) .map!(c => cast(char)c).array : iota(end, start + 1) .retro .map!(c => cast(char)c).array; }).join; } enum charRange = processSpec(spec); } unittest { assert(charRange!q{ 'a'..'z', 'A'..'Z', '0'..'9' } == charRange!q{ '9'..'0', 'Z'..'A', 'z'..'a' }.reverse); } void main(string[] argv) { auto t1 = charRange!q{ 'a'..'z', 'A'..'Z', '0'..'9' }; writeln(t1); auto t2 = charRange!q{ '9'..'0', 'Z'..'A', 'z'..'a' }; writeln(t2); }
Jan 22 2015