digitalmars.D.learn - Defining a static array with values in a range
- tcak (8/8) Jan 21 2015 I want to define alphanumeric characters in an easy way.
- Vlad Levenfeld (11/19) Jan 21 2015 This is interesting, I think this syntax would be pretty cool if
- anony (5/13) Jan 21 2015 There are convenient constants defined in std.ascii.
- tcak (5/21) Jan 22 2015 Well, that's just disguising what we can't do.
- bearophile (31/32) Jan 22 2015 When the a..b syntax was added to foreach() someone criticized
- Kagamin (3/13) Jan 22 2015 If you declare the string as immutable, the concatenation will be
- Jonathan M Davis via Digitalmars-d-learn (16/24) Jan 22 2015 std.range.iota is what's used to creat ranges of values, and std.array.a...
- bearophile (10/14) Jan 22 2015 Those ranges are probably open on the right.
- Jonathan M Davis via Digitalmars-d-learn (12/20) Jan 22 2015 They probably are actually, since open on the right is usually how thing...
- bearophile (4/5) Jan 22 2015 But the +1 changes the char to an int.
- Jonathan M Davis via Digitalmars-d-learn (8/11) Jan 22 2015 True, though iota doesn't seem to like to operate on char anyway, so fro...
- tcak (6/25) Jan 22 2015 So, at the end of the day (I left working on my Matcher class in
- bearophile (5/9) Jan 22 2015 Right. The 'a'..'d' is not first class, and you can't invent new
- Meta (28/32) Jan 22 2015 There is rarely never a way to do something in D, if you want to
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
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
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
On Thursday, 22 January 2015 at 07:29:05 UTC, anony wrote:On Thursday, 22 January 2015 at 05:56:40 UTC, tcak wrote: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.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 22 2015
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
On Thursday, 22 January 2015 at 09:26:21 UTC, tcak wrote:If you declare the string as immutable, the concatenation will be done at compile time.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
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
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
On Thursday, January 22, 2015 10:42:59 bearophile via Digitalmars-d-learn wrote:Jonathan M Davis:They probably are actually, since open on the right is usually how things are done, but that's easy fixed with some +1's.auto r = chain(uiota('a', 'z'), uiota('A', 'Z'), uiota('0', '9'));Those ranges are probably open on the right.Kagamin: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 DavisIf you declare the string as immutable, the concatenation will be done at compile time.In function-scope I think you need enum.
Jan 22 2015
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
On Thursday, January 22, 2015 15:16:07 bearophile via Digitalmars-d-learn wrote:Jonathan M Davis: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 Davisbut that's easy fixed with some +1's.But the +1 changes the char to an int.
Jan 22 2015
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: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.Jonathan M Davis: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 Davisbut that's easy fixed with some +1's.But the +1 changes the char to an int.
Jan 22 2015
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
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
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
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
On Thursday, 22 January 2015 at 19:00:47 UTC, zeljkog wrote:On 22.01.15 19:26, Meta wrote:By wrapping it in a template the array will always be generated at compile time, even if you assign it to a runtime variable.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
On 22.01.15 20:05, Meta wrote:On Thursday, 22 January 2015 at 19:00:47 UTC, zeljkog wrote:Yes, but then you can not use runtime spec.On 22.01.15 19:26, Meta wrote:By wrapping it in a template the array will always be generated at compile time, even if you assign it to a runtime variable.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
On Thursday, 22 January 2015 at 19:12:32 UTC, zeljkog wrote:On 22.01.15 20:05, Meta wrote:OP wanted it to be done at compile time.On Thursday, 22 January 2015 at 19:00:47 UTC, zeljkog wrote:Yes, but then you can not use runtime spec.On 22.01.15 19:26, Meta wrote:By wrapping it in a template the array will always be generated at compile time, even if you assign it to a runtime variable.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
On Thursday, 22 January 2015 at 19:13:46 UTC, Meta wrote:On Thursday, 22 January 2015 at 19:12:32 UTC, zeljkog wrote: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); }On 22.01.15 20:05, Meta wrote:OP wanted it to be done at compile time.On Thursday, 22 January 2015 at 19:00:47 UTC, zeljkog wrote:Yes, but then you can not use runtime spec.On 22.01.15 19:26, Meta wrote:By wrapping it in a template the array will always be generated at compile time, even if you assign it to a runtime variable.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