Re: How to loop through characters of a string in D language?
On Wednesday, 8 December 2021 at 11:23:45 UTC, BoQsc wrote: Let's say I want to skip characters and build a new string. The string example to loop/iterate: ``` import std.stdio; void main() { string a="abc;def;ab"; } ``` The character I want to skip: `;` Expected result: ``` abcdefab ``` import std.stdio : writeln; import std.algorithm.iteration : filter; import std.conv : to; void main() { string a = "abc;def;ab"; string b = a.filter!(c => c != ';').to!string; writeln(b); }
Re: How to imporve D-translation of these Python list comprehensions ?
On Monday, 15 January 2018 at 19:05:52 UTC, xenon325 wrote: I think, most clear code would be with tripple `foreach`, so I'll go with that. But probably someone will come up with something better and range-ier. I will admit clarity has suffered, but I like the brevity: import std.json : JSONValue; import std.array : array, assocArray; import std.range : enumerate, byPair; import std.algorithm : sort, joiner, map, uniq, each; import std.typecons : tuple; import std.conv : to; import std.stdio : writeln; unittest { immutable srv1 = ["acs": ["ver": "1.2.3", "rev": "6f2260d"], "cms": ["ver": "4.5", "rev": "b17a67e"], "ots": ["ver": "6.7.80", "rev": "4f487d2"]]; immutable srv2 = ["acs": ["ver": "1.2.3", "rev": "6f2260d"], "cms": ["ver": "5.1", "rev": "2a56c53"], "vaa": ["ver":"0.7", "rev": "00852cb"]]; immutable keys = ["rev", "ver"]; immutable srvs = [srv1, srv2]; alias aget = (name, key) => srvs.map!(s => s.get(name, [key:""])[key]); alias bget = (name, key) => aget(name, key).enumerate.map!(b => tuple(key~b.index.to!string, b.value)); alias merge = (aa1, aa2) => (aa2.byPair.each!(kv => aa1[kv.key] = kv.value), aa1); auto result = srvs .map!(s => s.byKey) .joiner .array .sort .uniq .map!(name => merge(keys.map!(key => bget(name, key)).joiner.assocArray, ["_name": name])) .array; writeln(JSONValue(result).toPrettyString()); } -- Simen
Re: How do I set a class member value by its name in a string?
On Wednesday, 27 December 2017 at 21:42:53 UTC, Mengu wrote: On Wednesday, 27 December 2017 at 21:39:49 UTC, Mengu wrote: On Wednesday, 27 December 2017 at 20:54:17 UTC, bitwise wrote: [...] there's also a simple workaround for fields with the same type: https://run.dlang.io/is/dsFajq import std.stdio; struct S { int x; int y; } auto setValue(ref S s, string field, int value) { foreach (fieldName; __traits(allMembers, S)) { if (fieldName == field) { __traits(getMember, s, fieldName) = value; break; } } } void main() { S s; s.setValue("x", 5); s.setValue("y", 25); writeln(s); } you can play with it to make it more generic. you can also create a mixin template that would generate setters for each field you would need a setter for and then in the run time you'd just be able to call them. return type should just be void. that's just my muscle memory. :-D More generic, for more better: void setValue(T, V)(auto ref T aggregate, string field, V value) { import std.traits : FieldNameTuple; import std.meta : Alias; switch (field) { foreach (fieldName; FieldNameTuple!T) { case fieldName: static if (is(typeof(__traits(getMember, aggregate, fieldName) = value))) { __traits(getMember, aggregate, fieldName) = value; return; } else assert(false, T.stringof ~ "."~field~" cannot be assigned from a "~V.stringof~"."); } default: assert(false, T.stringof ~ " has no field named "~field~"."); } } unittest { import std.exception : assertThrown; import core.exception : AssertError; static struct S { int x; string s; } S s; s.setValue("x", 14); assert(s.x == 14); assertThrown!AssertError(s.setValue("q", 14)); assertThrown!AssertError(s.setValue("s", 14)); s.setValue("s", "abc123"); assert(s.s == "abc123"); } unittest { import std.exception : assertThrown; import core.exception : AssertError; static class C { int x; string s; } C c = new C; c.setValue("x", 14); assert(c.x == 14); assertThrown!AssertError(c.setValue("q", 14)); assertThrown!AssertError(c.setValue("s", 14)); c.setValue("s", "abc123"); assert(c.s == "abc123"); (new C).setValue("x", 143); } -- Biotronic
Re: BitArray shift left/right confusion.
On Wednesday, 27 December 2017 at 18:08:19 UTC, Bastiaan Veelo wrote: I suppose the following is not a bug, but confusing it is: ``` void main() { import std.stdio; import std.bitmanip; BitArray ba = [1, 1, 1, 1, 1, 1, 1, 1]; writeln(ba);// [1, 1, 1, 1, 1, 1, 1, 1] ba >>= 4; // right shift writeln(ba);// [1, 1, 1, 1, 0, 0, 0, 0] bits shifted left }``` I suppose this is because the array is printed left-to-right, whereas the bits in a byte are typically ordered right-to-left. I suppose I should interpret the bits in the array to increase in significance with increasing index (little endian) and that right-shift means a shift towards less significance (which is to the right in big endian). The documentation of <<= and >>= [1] however just talks about left and right, without defining left and right or clarifying that the directions are reversed from how the array is printed. Is there something I have missed? [1] https://dlang.org/phobos/std_bitmanip.html#.BitArray.opOpAssign.2 BitArray is apparently a mess. As you've pointed out it prints the bits in the wrong order. I won't mince words here, since D has binary literals on the form 0b1000. Put that in a BitArray and print it with the format string "0b%b", and you'll get 0b0001. While it may have been intentional, it's bug prone and confusing, and so definitely a bug. It also fucks up royally when it has an exact multiple of 32 bits in its buffer, overwriting the last word with 0s when you try and shift it in any way. It also doesn't remove set bits outside of its covered area when cast to size_t[]. That is, if I do cast(size_t[])(BitArray([1,1,1,1,1,1,1,1]) << 4), the result will be something like [4080], which corresponds to [0b___]. Lastly (and this is pointed out explicitly in the documentation, but still smells if you ask me), it will overwrite bits in the words it covers, even if it does not cover those exact bits. The first two are definitely bugs. The last two are results of the intended use case for BitArray, I believe. The documentation doesn't explicitly point this out, but it seems BitArray is intended to give a bit-by-bit view of an area of memory that is actually some other type. Something like this: struct S { int n; float f; } void foo(ref S s) { import std.bitmanip; auto a = BitArray(()[0..1], S.sizeof); a[7] = true; // Actually sets the corresponding bit in s. } -- Biotronic
Re: Tuple Array Sorting
On Friday, 15 December 2017 at 17:24:33 UTC, Vino wrote: Hi Biotronic, I was able to find a solution using container array and also date formatting, below is the code, please do let me know if you find any issue, as i have tested the script and it is working as expected. Program: import std.algorithm: filter, map, sort, each; import std.container.array; import std.file: SpanMode, dirEntries, isDir ; import std.stdio: writeln,writefln; import std.typecons: Tuple, tuple; import std.datetime.systime: SysTime; import std.conv; void main () { auto FFs = ["C:\\Temp\\sapnas2\\BACKUP", "C:\\Temp\\sapnas2\\EXPORT"]; Array!(Tuple!(string, SysTime)) Sorted; foreach(d; FFs[]) { auto dFiles = Array!(Tuple!(string, SysTime))(dirEntries(d, Why specify Array!(Tuple!(string, SysTime)) here? The return value from map should be perfectly fine, and if you really want an array I'd suggest writing dirEntries(d, SpanMode.shallow) .filter!(a => a.isDir) .map!(a => tuple(a.name, a.timeCreated))) .array; It's shorter, easier to read, and the speed difference should be miniscule at best. The same comment applies to Sorted, above, which could be defined as Tuple!(string, SysTime)[]. SpanMode.shallow).filter!(a => a.isDir).map!(a => tuple(a.name, a.timeCreated))); foreach(i; dFiles[]){ Sorted ~= i; } No need for a foreach here, you can just do Sorted ~= dFiles. Sorted[].sort!((a,b) => a[1] > b[1]).each!(e => writefln!"%-63s %.20s"(e[0], e[1].to!string)); You seem to be sorting and printing the Sorted array once per folder. Going back to [3,5,1], [6,2,4] example, this will print 1,3,5,1,2,3,4,5,6. That is, it sorts [3,5,1], then prints the result. Then it sorts [1,3,5,6,2,4], and prints that. If you move the sorting and priting outside the loop (so it's the last line before leaving main), you should get the correct result, and and not waste time sorting the array multiple times. } } In total, the changes I propose would lead to this code: import std.algorithm: filter, map, sort, each; import std.array : array; import std.file: SpanMode, dirEntries, isDir ; import std.stdio: writefln; import std.typecons: Tuple, tuple; import std.datetime.systime: SysTime; import std.conv : to; void main () { auto FFs = ["C:\\Temp\\sapnas2\\BACKUP", "C:\\Temp\\sapnas2\\EXPORT"]; Tuple!(string, SysTime)[] Sorted; foreach(d; FFs[]) { auto dFiles = dirEntries(d, SpanMode.shallow) .filter!(a => a.isDir) .map!(a => tuple(a.name, a.timeCreated)) .array; Sorted ~= dFiles; } Sorted[].sort!((a,b) => a[1] > b[1]) .each!(e => writefln!"%-63s %.20s"(e[0], e[1].to!string)); } -- Biotronic
Re: overload
On Thursday, 14 December 2017 at 22:47:15 UTC, dark777 wrote: I know that this community is not from c ++, but for some time I studied how to do overload of ostream operators in c ++ and I even managed to get to this result, I got to this result in another post done here but I did not understand the that it returns this error below: bin2.cxx:44:43: error: expected ‘,’ or ‘...’ before ‘(’ token std::ostream& operator<<(std::ios_base& (__cdecl *_Pfn)(std::ios_base&)) ^ bin2.cxx: In member function ‘std::ostream& BinStream::operator<<(std::ios_base&)’: bin2.cxx:46:16: error: ‘_Pfn’ was not declared in this scope return os <<_Pfn; I expect it's confused by __cdecl. I just copied the function declaration from VS' iostreams, so it might be tainted by how VS does things. Removing __cdecl might work, or just add #define __cdecl __attribute__((__cdecl__)) A third option is to replace `std::ios_base& (__cdecl *Pfn)(std::ios_base&)` with `decltype(std::hex)& Pfn`. All of this said, I'd suggest finding a C++ forum to get answers to these questions. While I'm happy to help, it's not really the place for these discussions. -- Biotronic
Re: Tuple Array Sorting
On Tuesday, 12 December 2017 at 15:19:35 UTC, Vino wrote: import std.algorithm: filter, map, sort; import std.container.array; import std.file: SpanMode, dirEntries, isDir ; import std.stdio: writefln; import std.typecons: Tuple, tuple; import std.datetime.systime: SysTime; void main () { auto FFs = ["C:\\Temp\\sapnas2\\BACKUP", "C:\\Temp\\sapnas2\\EXPORT", "C:\\Temp\\sapnas2\\PROD_TEAM"]; Array!(Tuple!(string, SysTime)) Result; foreach(d; FFs[]) { auto dFiles = Array!(Tuple!(string, SysTime))(dirEntries(d, SpanMode.shallow).filter!(a => a.isDir).map!(a => tuple(a.name, a.timeCreated))); foreach(e; dFiles) { Result ~= e; } } writefln("%(%-(%-63s %.20s %)\n%)", Result[].sort!((a, b) => a[1] < b[1])); } Since there's little need to extract timeCreated and name before sorting, here's a version that doesn't: import std.algorithm : map, filter, sort; import std.array : array; import std.range : join; import std.file : SpanMode, dirEntries, isDir; import std.stdio : writefln; import std.typecons : tuple; void main() { auto folders = [`C:\Windows`, `C:\Program Files`, `C:\Users`]; auto sorted = folders .map!(f => f.dirEntries(SpanMode.shallow)) .join .filter!(e => e.isDir) .array .sort!((a,b) => a.timeCreated < b.timeCreated) .map!(e => tuple(e.name, e.timeCreated.toSimpleString[0 .. 20])); writefln("%(%-(%-63s %s %)\n%)", sorted); } And a version with normal loops, since the heavily range-based version above can be a bit dense. These programs do essentially the same thing: import std.algorithm : sort; import std.array : array; import std.file : SpanMode, dirEntries, DirEntry, isDir; import std.stdio : writefln; import std.typecons : tuple, Tuple; void main() { auto folders = [`C:\Windows`, `C:\Program Files`, `C:\Users`]; DirEntry[] subFolders; foreach (folder; folders) { auto children = dirEntries(folder, SpanMode.shallow); foreach (child; children) { if (child.isDir) subFolders ~= child; } } subFolders.sort!((a,b) => a.timeCreated < b.timeCreated); Tuple!(string, string)[] interestingParts; foreach (subFolder; subFolders) { interestingParts ~= tuple(subFolder.name, subFolder.timeCreated.toSimpleString[0..20]); } writefln("%(%-(%-63s %s %)\n%)", interestingParts); } As you can see, I'm just chopping off the parts I don't like from toSimpleString. It seems a good format function for dates does not exist in Phobos. -- Biotronic
Re: operator overload
On Tuesday, 12 December 2017 at 16:54:17 UTC, Biotronic wrote: There is no way in C++ to set the format the way you want it. If you want binary output, you need to call a function like your binario function. Of course this is not entirely true - there is a way, but it's ugly and probably not what you want: struct BinStream { std::ostream& os; BinStream(std::ostream& os) : os(os) {} template BinStream& operator<<(T&& value) { os << value; return *this; } BinStream& operator<<(int value) { os << binario(value); return *this; } std::ostream& operator<<(std::ios_base& (__cdecl *_Pfn)(std::ios_base&)) { return os << _Pfn; } }; struct Bin { friend BinStream operator<<(std::ostream& os, const Bin& f); } bin; BinStream operator<<(std::ostream& os, const Bin& f) { return BinStream(os); } int main() { std::cout << "\n\t127 em binario: " << binario(127) << "\n\t127 em binario: " << bin << 127 << "\n\t127 em octal: " << std::oct << 127 << "\n\t127 em binario: " << bin << 127 << "\n\t127 em hexadecimal: " << std::hex << 127 << "\n\t127 em binario: " << bin << 127 << "\n\t127 em decimal: " << std::dec << 127 << "\n\t127 em binario: " << bin << 127 << "\n\n"; } What is this black magic? Instead of overriding how std::ostream does formatting, Bin::Operator<< now returns a wrapper around a std::ostream, which special cases ints. If it gets any other format specifiers, it returns the ostream again, and the binary formatting is gone. All in all, I think the conclusion is: Stay with D. -- Biotronic
Re: operator overload
On Tuesday, 12 December 2017 at 15:52:09 UTC, dark777 wrote: I know that this community is not of c ++, but some time I have been studying how to do overload of ostream operators in c ++ and I even managed to get to this result but the same is not converting to binary only presents zeros as output to any number already tried to pass parameter of variable and yet he is not getting the number for conversion how to solve it so that it works correctly? PS: if I use the same conversion algorithm in a function it converts normally it is not only converting to the output of the operator .. https://pastebin.com/BXGXiiRk This line: << "\n\t127 em binario: " << bin << 127 << "\n\n"; Your hope is that the '<< bin' part should behave just like std::hex does, except binary instead of hex. Right? I'm afraid that's not how C++ formatting works. You can see a hint of what happens in the output you get: 127 em binario: 127 The zeroes are from the uninitialized int in your Bin struct, and the '127' is from the 127 you pass right after passing bin. Translating to code that does not use operators, the behavior you expect is something like this: std::cout.print("\n\t127 em binario: "); std::cout.setFormat(bin); std::cout.print(127); // Should use bin for formatting std::cout.print("\n\n"); The actual behavior is something like this: std ::cout.print("\n\t127 em binario: "); std::cout.print(bin); // Prints the value stored in bin. That is, 0. std::cout.print(127); // Prints 127 just the way it would otherwise print it. std::cout.print("\n\n"); There is no way in C++ to set the format the way you want it. If you want binary output, you need to call a function like your binario function. The point of overloading operator<<(std::ostream&, MyType) is not to format another type, but to be able to print MyType in a given way. Basically like toString() in D, C# or Java. -- Biotronic
Re: Tuple Array Sorting
On Monday, 11 December 2017 at 19:46:04 UTC, Vino wrote: import std.algorithm; import std.container.array; import std.file: SpanMode, dirEntries, isDir ; import std.stdio: writefln, writeln; import std.typecons: Tuple, tuple; import std.range: chain; void main () { auto FFs = ["C:\\Temp\\BACKUP", "C:\\Temp\\EXPORT", "C:\\Temp\\PROD_TEAM"]; foreach(d; FFs[]) { auto dFiles = Array!(Tuple!(string, SysTime))(dirEntries(d, SpanMode.shallow).filter!(a => a.isDir).map!(a => tuple(a.name, a.timeCreated))); writefln("%(%-(%-63s %s %)\n%)", chain(dFiles[]).sort!((a, b) => a[1] < b[1])); } } So let's go through this code then, shall we? Edited a bit for clarity: // For each directory (*one at a time*), foreach(d; FFs[]) { // List all the folders auto dFiles = Array!(Tuple!(string, SysTime))( dirEntries(d, SpanMode.shallow) // Not the files, .filter!(a => a.isDir) // Grab only the information we want, .map!(a => tuple(a.name, a.timeCreated))); // And print a sorted list of the subfolders of the current folder. writefln("%(%-(%-63s %s %)\n%)", chain(dFiles[]).sort!((a, b) => a[1] < b[1])); } This will go through C:\Temp\BACKUP, and display a sorted list of all the subfolders in that folder. Then it will do the same for C:\Temp\EXPORT, and then for C:\Temp\PROD_TEAM. No subfolder of C:\Temp\PROD_TEAM will be displayed before anything in C:\Temp\BACKUP, because you're sorting only one set of subfolders at a time. What you're essentially doing, is sorting [6,2,4] and [3,5,1] separately, and printing them separately, giving the illusion of having sorted them into the array [2,4,6,1,3,5]. As you correctly point out, this is not what you wanted. The code I presented avoids this by joining the lists for all the folders. Your code could also be simplified a bit - there's no need for the call to chain, and calling dirEntries.filter.map.array is easier to read and write than having Array!(Tuple!(string, SysTime)) at the front. In case my code from earlier was hard to understand, here's another version~, more closely modeled to your code: import std.algorithm : map, filter, sort; import std.array : array; import std.file : SpanMode, dirEntries, isDir; import std.stdio : writefln; import std.typecons : Tuple, tuple; import std.datetime : SysTime; void main() { // The folders we want to look in. auto folders = [`C:\Windows`, `C:\Program Files`, `C:\Users`]; // We'll put all the subfolders we find here, so we can sort them in the end. Tuple!(string, SysTime)[] subFolders; // Look through each folder in turn. foreach (folder; folders) { // Get all entries in the folder, auto entries = dirEntries(folder, SpanMode.shallow); // Get rid of files, auto folderEntries = entries.filter!(a => a.isDir); // Grab the interesting parts, auto interestingParts = folderEntries.map!(a => tuple(a.name, a.timeCreated)); // And add it to the array. subFolders ~= interestingParts.array; } // Sort the entire array. auto sorted = subFolders.sort!((a, b) => a[1] < b[1]); // And print it! writefln("%(%-(%-63s %s %)\n%)", sorted); }
Re: Tuple Array Sorting
On Monday, 11 December 2017 at 15:33:08 UTC, Vino wrote: On Monday, 11 December 2017 at 15:15:47 UTC, Biotronic wrote: On Monday, 11 December 2017 at 14:52:35 UTC, Vino wrote: Example Program and Output import std.algorithm: filter, map, sort; import std.container.array; import std.file: SpanMode, dirEntries, isDir ; import std.range: chain; import std.stdio: writefln; import std.typecons: Tuple, tuple; void main () { auto FFs = Array!(string)("C:\\Temp\\BACKUP", "C:\\Temp\\EXPORT", "C:\\Temp\\PROD_TEAM"); int AgeSize = 2; foreach(d; FFs[]) { auto dFiles = Array!(Tuple!(string, string))(dirEntries(d, SpanMode.shallow).filter!(a => a.isDir).map!(a => tuple(a.name, a.timeCreated.toSimpleString[0 .. 20]))); writefln("%(%-(%-63s %s %)\n%)", chain(dFiles[]).sort!((a,b) => a[0] > b[0])); } } You're somewhat close. You're sorting based on the 0th element of your tuples, while you should sort on the 1st. Something like this: import std.algorithm: filter, map, sort; import std.array : array; import std.file: SpanMode, dirEntries, isDir ; import std.range: join; import std.stdio: writefln; import std.typecons: tuple; void main () { auto folders = ["D:\\Dev"]; auto sorted = folders .map!(a => dirEntries(a, SpanMode.shallow)) .join .filter!(a => a.isDir) .map!(a => tuple(a.name, a.timeCreated.toSimpleString[0 .. 20])) .array .sort!((a,b) => a[1] > b[1]); writefln("%( %( %-63s %s %) \n%)", sorted); } -- Biotronic Hi, I tired that but no luck, below is the output, in your code you have one folder "auto folders = ["D:\\Dev"];" if you have multiple folder then output is not sorted. C:\Temp\BACKUP\dir2 2017-Sep-09 22:44:11 C:\Temp\BACKUP\dir1 2017-Sep-06 16:06:42 C:\Temp\BACKUP\DND3 2017-Sep-05 14:31:00 C:\Temp\BACKUP\t1 2017-Dec-11 04:10:02 C:\Temp\BACKUP\dir3 2017-Dec-10 06:56:07 C:\Temp\EXPORT\DND6 2017-Sep-05 14:31:00 C:\Temp\PROD_TEAM\DND1 2017-Sep-05 14:31:01 Are you sure that's the output from my code? Let's step through the code: // Iterating over folders: folders // Create a range where each element is a range of DirEntry // in the given folder. .map!(a => dirEntries(a, SpanMode.shallow)) // Join these together to a single range of DirEntry instead of // a range-of-ranges-of-DirEntry. .join // Remove anything that's not a folder. .filter!(a => a.isDir) // Grab the information we actually care about. .map!(a => tuple(a.name, a.timeCreated)) // Enumerate to an array, so we can sort it. .array // Sort this array by the second tuple element (timeCreated). .sort!((a,b) => a[1] > b[1]); If this code does not do what you're asking, there's a bug outside of the code, probably in the standard library. If instead you are invoking the program multiple times with a single folder each time, the output you describe is to be expected. Apart from that, I'm not sure what could be wrong. -- Biotronic
Re: Tuple Array Sorting
On Monday, 11 December 2017 at 15:33:08 UTC, Vino wrote: I tired that but no luck, below is the output, in your code you have one folder "auto folders = ["D:\\Dev"];" if you have multiple folder then output is not sorted. Works on my machine. Of course, since time toSimpleString returns non-ISO8601, the sorting might not make perfect sense ("2017-Dec-12" < "2017-Jan-01"). Testing with multiple folders gives perfectly sorted output. If you want it sorted by date instead of by string representation of date, feel free to remove .toSimpleString[0 .. 20]. -- Biotronic
Re: Tuple Array Sorting
On Monday, 11 December 2017 at 14:52:35 UTC, Vino wrote: Example Program and Output import std.algorithm: filter, map, sort; import std.container.array; import std.file: SpanMode, dirEntries, isDir ; import std.range: chain; import std.stdio: writefln; import std.typecons: Tuple, tuple; void main () { auto FFs = Array!(string)("C:\\Temp\\BACKUP", "C:\\Temp\\EXPORT", "C:\\Temp\\PROD_TEAM"); int AgeSize = 2; foreach(d; FFs[]) { auto dFiles = Array!(Tuple!(string, string))(dirEntries(d, SpanMode.shallow).filter!(a => a.isDir).map!(a => tuple(a.name, a.timeCreated.toSimpleString[0 .. 20]))); writefln("%(%-(%-63s %s %)\n%)", chain(dFiles[]).sort!((a,b) => a[0] > b[0])); } } You're somewhat close. You're sorting based on the 0th element of your tuples, while you should sort on the 1st. Something like this: import std.algorithm: filter, map, sort; import std.array : array; import std.file: SpanMode, dirEntries, isDir ; import std.range: join; import std.stdio: writefln; import std.typecons: tuple; void main () { auto folders = ["D:\\Dev"]; auto sorted = folders .map!(a => dirEntries(a, SpanMode.shallow)) .join .filter!(a => a.isDir) .map!(a => tuple(a.name, a.timeCreated.toSimpleString[0 .. 20])) .array .sort!((a,b) => a[1] > b[1]); writefln("%( %( %-63s %s %) \n%)", sorted); } -- Biotronic
Re: Optimizing a bigint fibonacci
On Wednesday, 6 December 2017 at 10:16:16 UTC, helxi wrote: On Wednesday, 6 December 2017 at 10:00:48 UTC, Biotronic wrote: AliasSeq!(a, b) = tuple( a * (2*b - a), a*a + b*b); [...] Nice. But why the AliasSeq? Just playing around a bit. The alternative is to assign to a temporary: auto c = a * (2*b - a); b = a*a + b*b;' a = c; The resulting behavior is the same, and apart from the cludgy names I kinda like doing (a,b) = (c,d). -- Biotronic
Re: Optimizing a bigint fibonacci
On Wednesday, 6 December 2017 at 09:12:08 UTC, helxi wrote: This is question not directly related to language concepts, it's got more to do with the application. I would appreciate if anyone would point to me how I could optimize this bit of code Here's my version:, based on fast squaring: auto fib(ulong n) { import std.bigint : BigInt; import std.meta : AliasSeq; import std.typecons : tuple; BigInt a = 0; BigInt b = 1; auto bit = 63; while (bit > 0) { AliasSeq!(a, b) = tuple( a * (2*b - a), a*a + b*b); if (n & (BigInt(1) << bit)) { AliasSeq!(a, b) = tuple(b, a+b); } --bit; } return a; } unittest { import std.stdio : writeln; import std.datetime : MonoTime; auto t0 = MonoTime.currTime; writeln(fib(10_000_000)); writeln(MonoTime.currTime - t0); } Takes around 600 milliseconds to compute fib(1_000_000), and 50 seconds for fib(10_000_000). Fast squaring algorithm found and described here: https://www.nayuki.io/page/fast-fibonacci-algorithms I also noticed that if I try to compute it in the compile time with enum x = fib(10) the compiler freezes. What could cause this? That's because the poor compiler isn't as good at optimizing compile-time code as run-time code, and because fib(10) is frigging ginormous. -- Biotronic
Re: Sort characters in string
On Wednesday, 6 December 2017 at 08:59:09 UTC, Fredrik Boulund wrote: string word = "longword"; writeln(sort(word)); But that doesn't work because I guess a string is not the type of range required for sort? Yeah, narrow (non-UTF-32) strings are not random-access, since characters like take up more than one code unit, and so ""[0] returns an invalid piece of a character instead of a full character. In addition, sort does in-place sorting, so the input range is changed. Since D strings are immutable(char)[], changing the elements is disallowed. So in total, you'll need to convert from a string (immutable(char)[]) to a dchar[]. std.conv.to to the rescue: import std.stdio : writeln; import std.conv : to; import std.algorithm.sorting : sort; string word = "longword"; writeln(sort(word.to!(dchar[]))); // dglnoorw -- Biotronic
Re: cannot deduce template lambda from argument
On Tuesday, 5 December 2017 at 23:01:43 UTC, aliak wrote: immutable lambda(T) = (T n) => n * n; Generally, you'd want to write alias lambda = n => n * n; instead. That said, I don't see any reason why your syntax shouldn't work, and would argue it's a bug. Please file it in Bugzilla. -- Biotronic
Re: Turn a float into a value between 0 and 1 (inclusive)?
On Tuesday, 21 November 2017 at 09:21:29 UTC, Chirs Forest wrote: I'm interpolating some values and I need to make an (elapsed_time/duration) value a float between 0 and 1 (inclusive of 0 and 1). The elapsed_time might be more than the duration, and in some cases might be 0 or less. What's the most efficient way to cap out of bounds values to 0 and 1? I can do a check and cap them manually, but if I'm doing a lot of these operations I'd like to pick the least resource intensive way. Good old comparisons should be plenty in this case: T clamp(T)(T value, T min, T max) { return value < min ? min : value > max ? max : value; } That's two comparisons and two conditional moves. You're not gonna beat that. Also, if I wanted out of bounds values to wrap (2.5 becomes 0.5) how would I get that value and also have 1.0 not give me 0.0? As Petar points out, this is somewhat problematic. You think of the codomain (set of possible results) as being the size (1.0 - 0.0) = 1.0. However, since you include 1.0 in the set, the size is actually 1.0 + ε, which is very slightly larger. You could use a slightly modified version of Petar's function: T zeroToOne(T)(T val) { return val % (1+T.epsilon); } However, this induces rounding errors on larger numbers. e.g. 1.0 + ε becomes 0, while you'd want it to be ε. I guess I should ask for some clarification - what would you expect the return value to be for 2.0? Is that 0.0 or 1.0? How about for -1.0? A simple function that does give the results you want between 0.0 and 1.0 (inclusive) is this: T zeroToOne(T)(T val) { if (val >= 0 && val <= 1.0) { return val; } return val % 1; } The problem now, however, is that zeroToOne gives negative results for negative values. This is probably not what you want. This can be fixed with std.math.signbit: T zeroToOne(T)(T val) { import std.math : signbit; if (val >= 0 && val <= 1.0) { return val; } return (val % 1) + val.signbit; } If you're willing to give up the last 1/8388608th of precision (for float, 1/4503599627370496th for double) that including 1.0 gives you, this can become: T zeroToOne(T)(T val) { import std.math : signbit; return (val % 1) + val.signbit; } Which is easier to read, and consistent for values outside the [0,1) interval. -- Biotronic
Re: Missing return value error not present with template
On Wednesday, 15 November 2017 at 08:43:01 UTC, Tony wrote: Error: function test_warnings.MyClass.SomeMethod has no return statement, but is expected to return a value of type int but if I make it a template class: class MyClass(T) { there is no compile error. I don't know why the error isn't given for the template code as well. As Andrea points out, it's because it's a template. There are reasons for this that may not be immediately obvious, though. Static if is one: class Foo(T) { int bar() { static if (is(T == int)) { return 0; } else static if (is(T == string)) { return 1; } } } In the general case, bar() would be missing a return statement. However, if the only uses are when T is either a string or an int, that's no problem, since the failure case doesn't see use. Another example is mixins: class Foo(string s) { int bar() { mixin(s); } } The compiler now has absolutely no idea what bar() might or might not return, until s is specified. If s is "return 3;", everything's hunky-dory. If it's "return string.init;", bar will fail to compile. It's reasonable for the type to only accept correct code, so this is a non-problem in practice. Next up, overloads: class Foo(T) { int bar() { return baz(T.init); } } int baz(int n) { return n; } void baz(string s) {} Members of the template argument: class Foo(T) { int bar() { T.baz(); } } There's plenty other cases where the compiler simply cannot tell if the code is invalid without instantiating the template, and where it's valid in some cases but not others. There are other languages that require the equivalent of template parameters to conform to some defined meta-type, and there are benefits to both sides of that discussion. D has chosen to go the loosely (or duck-) typed way, and I feel it's a good match for the rest of the language. -- Biotronic
Re: Disabled and enabled copy constructors and .dup
On Tuesday, 24 October 2017 at 11:37:42 UTC, Per Nordlöw wrote: On Tuesday, 24 October 2017 at 07:56:34 UTC, Biotronic wrote: struct SuppressPostblit(T) { // Disguise T as a humble array. private ubyte[T.sizeof] _payload; ... A bit too hackish for my taste, but does the job still. Thanks. Cleaned up and slightly less hackish (yeah, it bothered me too): enum SuppressOptions { destructor = 1, postblit = 2 } struct Suppress(T, SuppressOptions options) if (options != 0) { import std.traits : isCopyable; private enum suppressPostblit = (options & SuppressOptions.postblit) != 0; private enum suppressDestructor = (options & SuppressOptions.destructor) != 0; private enum postblitName = __traits(hasMember, T, "__xpostblit") ? "__xpostblit" : "__postblit"; // Disguise T as a humble array. private ubyte[T.sizeof] _payload; // Create from instance of T. this(T arg) { _payload = *cast(ubyte[T.sizeof]*) } // Or forward constructor arguments to T's constructor. static if (__traits(hasMember, T, "__ctor")) { this(Args...)(Args args) if (__traits(compiles, (Args e){__traits(getMember, T.init, "__ctor")(e);})) { __traits(getMember, get, "__ctor")(args); } } // Call dtor static if (!suppressDestructor) { ~this() { destroy(get); } } // Call postblit static if (!suppressPostblit) { static if (!isCopyable!T) { @disable this(this); } else static if (__traits(hasMember, T, postblitName)) { this(this) { __traits(getMember, get, postblitName)(); } } } // Pretend to be a T. @property ref T get() { return *cast(T*)_payload.ptr; } alias get this; } struct S1 { @disable this(this); ~this() { throw new Exception("Don't touch my destructor!"); } } unittest { import std.exception; static assert(!__traits(compiles, (Suppress!S1 a) { auto b = a; })); static assert(__traits(compiles, (Suppress!(S1, SuppressOptions.postblit) a) { auto b = a; })); assertThrown({ Suppress!(S1, SuppressOptions.postblit) a; }()); assertNotThrown({ Suppress!(S1, SuppressOptions.postblit | SuppressOptions.destructor) a; }()); } -- Biotronic
Re: Disabled and enabled copy constructors and .dup
On Tuesday, 24 October 2017 at 07:33:43 UTC, Per Nordlöw wrote: If I have a `struct X` (container in my case) with disabled copying (postblit) and instead a .dup property, is it somehow possible, unsafe or not, to have `X` as a member of another `struct Y` with an enabled copy constructor which calls `X.dup`? With the same approach outline in https://forum.dlang.org/post/nakguitssvjdclpgw...@forum.dlang.org, it is indeed possible. In fact, simply using SuppressGC!X should enable it. Note however, that since the point of SuppressGC is to not call the object's destructor (and thus its name is poorly chosen by yours truly), you will need to do so explicitly. Updating the approach to only suppress postblits: struct SuppressPostblit(T) { // Disguise T as a humble array. private ubyte[T.sizeof] _payload; // Create from instance of T. this(T arg) { _payload = *cast(ubyte[T.sizeof]*) } // Or forward constructor arguments to T's constructor. static if (__traits(hasMember, T, "__ctor")) { this(Args...)(Args args) if (__traits(compiles, (Args e){__traits(getMember, T.init, "__ctor")(e);})) { __traits(getMember, get, "__ctor")(args); } } // Pretend to be a T. @property ref T get() { return *cast(T*)_payload.ptr; } alias get this; static if (__traits(hasMember, T, "__dtor")) { ~this() { __traits(getMember, get, "__dtor")(); } } } -- Simen
Re: Skynet 1M Fiber microbenchmark in D
On Wednesday, 18 October 2017 at 12:32:31 UTC, Nordlöw wrote: Further, are we forced to use the GC for Fiber allocation or can a sub-class of Fibers implement its own allocation strategy? Afraid it's set in stone. Now, it doesn't actually use the GC for allocating the stack memory, instead opting for VirtualAlloc on Windows and mmap, valloc or malloc on other platforms. Of course, it's possible to change the implementation in core.thread, but it's probably more work than you want. Copying the entire Fiber class and changing it also runs into some problems, since it depends on private stuff in core.thread. -- Biotronic
Re: Skynet 1M Fiber microbenchmark in D
On Wednesday, 18 October 2017 at 11:34:57 UTC, Nordlöw wrote: Another thing...how should the synchronization between the fibers figure out when the total number of fibers have reached one million?...via an atomic counter fed by reference to the constructor...or are there better ways? Because I do need an atomic reference counter here, right? This is how I did it: import core.thread : Fiber; class MyFiber : Fiber { int _depth; ulong _index; ulong _value; this(int depth, ulong index) { super(); _depth = depth; _index = index; } void run() { if (_depth == 6) { // 10^6 == 1 million, so stop here. _value = _index; return; } _value = 0; foreach (i; 0..10) { // Line 23 auto e = new MyFiber(_depth+1, _index * 10 + i); e.call(); _value += e._value; } } } unittest { import std.stdio : writeln; import std.datetime.stopwatch : StopWatch, AutoStart; auto sw = StopWatch(AutoStart.yes); auto a = new MyFiber(0, 0); a.call(); sw.stop(); assert(a._value == 4950); writeln(a._value, " after ", sw.peek); } And how do I parallelize this over multiple worker threads? AFAICT fibers are by default all spawned in the same main thread, right? True. Well, they're not really spawned on any thread - they're allocated on the heap, have their own stack, and are run on whichever thread happens to invoke their call() method. I experimented a little bit with parallelism, and the easiest definitely is to replace line 23 with this: foreach (i; taskPool.parallel(10.iota, 1)) { It seems to make very little difference in terms of run time, though. I tried using a mix of these approaches - parallel at low depth, basically just to fill up the cores, and serial closer to the leaves. The difference is still negligible, so I assume the losses are elsewhere. -- Biotronic
Re: Making template instantiations more lazy
On Wednesday, 18 October 2017 at 10:55:49 UTC, Per Nordlöw wrote: On Wednesday, 18 October 2017 at 10:36:41 UTC, Per Nordlöw wrote: Yeah I've thought of that. I still would like to have it built-in to the compiler. Would such a change cause any serious breakage? Seems unlikely - when did you last use a function without using it? :p I'm not actually sure why D behaves this way - C++ doesn't. I guess there is some value as tests - instantiating the type tests that all its methods compile. Not actually sure that's more of a positive than a negative, but it's certainly never bothered me. -- Biotronic
Re: Skynet 1M Fiber microbenchmark in D
On Wednesday, 18 October 2017 at 11:01:56 UTC, Per Nordlöw wrote: On Wednesday, 18 October 2017 at 09:01:30 UTC, Per Nordlöw wrote: Creates an actor (goroutine, whatever), which spawns 10 new actors, each of them spawns 10 more actors, etc. until one million actors are created on the final level. Then, each of them returns back its ordinal number (from 0 to 99), which are summed on the previous level and sent back upstream, until reaching the root actor. (The answer should be 4950). See also: https://github.com/atemerev/skynet I Fibers aren't supposed to take any parameters how are we supposed to pass values to it during creation? class MyFiber : Fiber { this(int arguments, string go, float here) { super(); // Save arguments somewhere } void run() { // Use arguments here. } } -- Biotronic
Re: Making template instantiations more lazy
On Wednesday, 18 October 2017 at 09:56:33 UTC, Nordlöw wrote: On Wednesday, 18 October 2017 at 09:32:39 UTC, Jonathan M Davis wrote: On Wednesday, October 18, 2017 09:13:47 Per Nordlöw via Digitalmars-d-learn wrote: Are there any nearby plans to make more template instantiations (especially aggregate members) lazy in DMD? Are there any specific obstacles against doing that or has it just not been prioritized? Templates are never instantiated unless they're actually... well, instantiated. So, they're already lazy in that sense. The compiler isn't pre-emptive with them at all. What exactly about them do you want to be lazy that isn't? - Jonathan M Davis Member functions of templated containers is my main focus. Make them templates, that should solve the problem: struct S(T) { void foo()() { compileerror; } }
Re: testing if data is allocated on the stack or heap
It's worth pointing out, btw, that the main reason for this code is to help drug diagnose his or her problem, not to be the be-all, end-all of stack identifying functions. :) It will of course not correctly identify pointers to variables on other threads' stacks, and fiber stacks probably trip it up bad. -- Biotronic
Re: testing if data is allocated on the stack or heap
On Tuesday, 17 October 2017 at 23:59:19 UTC, Steven Schveighoffer wrote: On 10/17/17 7:32 PM, flamencofantasy wrote: On Tuesday, 17 October 2017 at 17:27:17 UTC, Biotronic wrote: On Tuesday, 17 October 2017 at 15:33:02 UTC, drug wrote: [...] I have very little knowledge about sbrk, so here's my solution. Tested on win32 and win64. [...] Try this; unittest { int[5*1024] n; int* p = new int; assert(n.onStack); assert(!p.onStack); } The second is wrong. You are asserting that the storage of p is not on the stack, but it is. What p *points at* is not on the stack. So the correct call should be: assert(!(*p).onStack); or (IMO) less ugly: assert(!onStack(*p)); -Steve No, the second is correct - the ref overload is only chosen when p is not implicitly castable to void*. In fact, the second assert passes, it's the first one that causes problems. I've written an improved version, that probably still has problems. If flamencofantasy would like to point out other shortcomings, I'd love to learn. module stackCheck; private size_t stackStart; enum size_t pageSize = 0x1000; static this() { import core.stdc.stdlib : alloca; stackStart = cast(size_t)alloca(size_t.sizeof); } bool onStack(void* p) { size_t end = cast(size_t) size_t pp = cast(size_t)p; size_t ss = stackStart; if (end > ss) { end &= ~(pageSize-1); ss &= ~(pageSize-1); end += pageSize; return pp >= ss && pp <= end; } else { end &= ~(pageSize-1); ss &= ~(pageSize-1); ss += pageSize; return pp <= ss && pp >= end; } } bool onStack(T)(ref T p) { return ().onStack; } unittest { int n; int* p = new int; assert(n.onStack); assert(!p.onStack); } unittest { int[5*1023] n; int* p = new int; assert(n.onStack); assert(!p.onStack); }
Re: testing if data is allocated on the stack or heap
On Tuesday, 17 October 2017 at 15:33:02 UTC, drug wrote: My code fails and I guess the reason is I have a slice to data in the stack and it becomes garbage in some moment. So I need a way to check where data is placed. Is it right that it can be done in linux using `sbrk` so that if the addr of data is less than `sbrk(0)` returning then data is on the stack and on the heap in other case? I have very little knowledge about sbrk, so here's my solution. Tested on win32 and win64. module stackCheck; private size_t stackStart; enum size_t pageSize = 0x1000; static this() { import core.stdc.stdlib : alloca; stackStart = cast(size_t)alloca(size_t.sizeof) & ~(pageSize-1); } bool onStack(void* p) { size_t end = (cast(size_t) & ~(pageSize-1)) + pageSize; size_t pp = cast(size_t)p; if (end > stackStart) { return pp >= stackStart && pp <= end; } else { return pp <= stackStart && pp >= end; } } bool onStack(T)(ref T p) { return ().onStack; } unittest { int n; int* p = new int; assert(n.onStack); assert(!p.onStack); } -- Biotronic
Re: partiallyQualifiedName?
On Monday, 16 October 2017 at 23:56:00 UTC, Nicholas Wilson wrote: using fullyQualifiedName [here] (https://github.com/libmir/dcompute/blob/master/source/dcompute/driver/ocl/util.d#L120) leads to a large compilation slowdown, but I only need it to disambiguate up to the module level i.e. so that struct Context { enum Properties {} static struct Info { @(0) Properties p; // <--- } } ... partiallyQualifiedName!p ... resolves to Context.Properties instead of dcompute.driver.ocl.context.Context.Properties, thus avoiding many template instantiations. Alas typeof(p).stringof, which yields Properties, errors "No such identifier Properties" (and subsequently crashes the CTFE engine). I tried looking at the fullyQualifiedName but got lost pretty quick. If I understand things correctly, you only care about enums nested in scopes up to the module scope, right? If so, this seems to fit the bill: enum A {a} struct S { enum B {b} struct S2 { enum C {c} C c; } A a; B b; int n, m; pragma(msg, partiallyQualifiedName!n); // S.n pragma(msg, partiallyQualifiedName!(S2)); // S.S2 pragma(msg, partiallyQualifiedName!(typeof(a))); // A pragma(msg, partiallyQualifiedName!(typeof(b))); // S.B pragma(msg, partiallyQualifiedName!(typeof(S2.c))); // S.S2.C pragma(msg, partiallyQualifiedName!(a)); // S.a pragma(msg, partiallyQualifiedName!(b)); // S.b pragma(msg, partiallyQualifiedName!(S2.c)); // S.S2.c } template isModule(alias a) { static if (is(a) || is(typeof(a)) || a.stringof.length < 7) { enum isModule = false; } else { enum isModule = a.stringof[0..7] == "module "; } } template partiallyQualifiedName(alias a) { static if (isModule!a) { enum partiallyQualifiedName = ""; } else { static if (!isModule!(__traits(parent, a))) { enum prefix = partiallyQualifiedName!(__traits(parent, a)) ~ "."; } else { enum prefix = ""; } enum partiallyQualifiedName = prefix ~ __traits(identifier, a); } } Note that it fails for built-in types, arrays, and many other cases, and does not support const/immutable/shared/etc. It should cover the cases described, though, and that's what's most important. If more support is needed, consider it a starting point, and feel free to ask for more. :) -- Biotronic
Re: what operator(s) should I overload to prevent this?
On Monday, 16 October 2017 at 12:00:13 UTC, drug wrote: I refactored `MyStructure` added own implementation of malloced array based on pureRealloc/pureFree instead of dynamic array I used before and now I have error: Error: cannot implicitly convert expression get(msg.getData()) of type const(MyStructure) to MyStructure. What operators should I overload to let this conversion? I've tryed opCast and it fails in other call sites, also I've tried opAssign - it didn't works, what worked is wrapping in ctor - MyStruct(const_var). But using ctor is not convenient. The reason this doesn't work is you have some sort of non-immutable pointer, reference or array in your struct. I'm not sure why this worked before, since I have no idea what your code looks like. Hence, I can only give limited advice. Notably, the reasons this gives you a warning is because it would be unsafe to do a simple cast. You might have turned immutable(T)[] into T[] (where T is int, byte, char, what-have-you). If this is the case, consider why this happened, and try to turn it back into immutable(T)[]. The same goes for immutable(T)*. Remember that an alias can make something immutable, so it might not be immediately obvious. If you're fine with being unsafe, and can't find a solution like the above, consider this: struct MyStruct { MyStruct getUnsafe() const { return cast(MyStruct)this; } alias getUnsafe this; } -- Biotronic
Re: Temporary objects as function parameters or when-is-this-shit-going-to-end?
On Friday, 13 October 2017 at 10:35:56 UTC, Jack Applegame wrote: Compiler creates struct on the stack and silently (without postblitting and destruction old object) moves it to another address. Is it normal? I don't think so. It is. Structs have no identity, and the compiler/GC/whatever is free to copy and/or move them about as it sees fit (as long as there is ostensibly only one - no duplicate constructor/destructor calls, no desynching of state). That's why the documentation[1] says not to have internal pointers in structs. WAT??? Compiler creates struct on the stack copies it without postblitting and destructs both objects. Now this looks like a real bug. There should be a this(this) call in there. Can I donate to the D Foundation and that my donations would be aimed at fixing exactly these bugs? BountySource[2] lets you do basically exactly that. [1]: https://dlang.org/spec/garbage.html, "Do not have pointers in a struct instance that point back to the same instance." [2]: https://www.bountysource.com/
Re: For fun: Expressive C++ 17 Coding Challenge in D
On Wednesday, 4 October 2017 at 19:20:12 UTC, Jesse Phillips wrote: On Wednesday, 4 October 2017 at 15:26:02 UTC, Ali Çehreli wrote: On 10/04/2017 02:04 AM, Biotronic wrote: ... Hey where is the list of features used e.g: ranges, ufcs... Features used: D. But sure, added them to the gist: https://gist.github.com/Biotronic/0bc6048b880d67bfdca970453cc47cf9 Also added some more stuff to show off more D features, like unittests and @safe. -- Biotronic
Re: For fun: Expressive C++ 17 Coding Challenge in D
On Wednesday, 4 October 2017 at 09:04:58 UTC, Biotronic wrote: Since the code uses ranges though, a simple replacement of readText with an mmapped equivalent should enable humongous file support with no other code change required. Drop-in replacement for readText: struct MmText { import std.mmfile; ulong _fileOffset; MmFile _file; this(string filename) { _file = new MmFile(filename); } dchar front() { auto end = min(_file.length, _fileOffset+4); auto data = cast(string)_file[_fileOffset..end]; return decodeFront(data); } void popFront() { auto end = min(_file.length, _fileOffset+4); auto data = cast(string)_file[_fileOffset..end]; size_t bytes; decodeFront(data, bytes); _fileOffset += bytes; } bool empty() { return _fileOffset >= _file.length; } } -- Biotronic
Re: How to implement `isTemplate` traits?
On Wednesday, 4 October 2017 at 09:32:31 UTC, drug wrote: I need to separate templates: ``` foreach(member; __traits(allMembers, V)) { static if (__traits(compiles, { auto _val = &__traits(getMember, value, member); }) { // a template needs to be instantiated to be addressable, so it works, but I think it's dirty hack instead of dry and clean way... } } ``` May be phobos has such traits somewhere? template isTemplate(T...) if (T.length == 1) { enum isTemplate = __traits(isTemplate, T[0]); } -- Biotronic
Re: For fun: Expressive C++ 17 Coding Challenge in D
On Tuesday, 3 October 2017 at 19:25:56 UTC, Ali Çehreli wrote: Found on Reddit: https://www.reddit.com/r/programming/comments/740617/the_expressive_c17_coding_challenge/ How would you do it in D? Ali P.S. You can ignore the following note from the challenge text; I don't think it applies to D. Honestly, I don't think it matters for C++17 either. :) "You can assume input files won't be super large and can fit fully into memory." https://gist.github.com/Biotronic/0bc6048b880d67bfdca970453cc47cf9 I opted for writing to stdout instead, because 1) it's easier, x) it's less code, and b) it's more flexible. The memory limitations certainly do apply - readText would fail upon reading humongous files, and for 32-bit programs the resulting string wouldn't be able to hold enough data. Since the code uses ranges though, a simple replacement of readText with an mmapped equivalent should enable humongous file support with no other code change required. -- Biotronic
Re: Struct bug?
On Monday, 2 October 2017 at 09:34:29 UTC, Andrea Fontana wrote: Anyway: you cant put a default destructor on struct True. In which case you should either @disable this() (which presents its own set of issues) or hide b behind a @property function, something like: struct S { B _b; @property B b() { if (_b is null) _b = new B(); return b; } } This exact same issue also crops up for classes, since typeid(T).initializer is simply blitted over the newly allocated memory. At least for classes we could change the language such that: class C { int[] p = new int[5]; } is sugar for: class C { int[] p; this() { p = new int[5]; } } No such solution exists for structs, since they don't have default constructors. -- Biotronic
Re: Struct bug?
On Monday, 2 October 2017 at 08:47:47 UTC, Andrea Fontana wrote: Why this code doesn't write two identical lines? https://dpaste.dzfl.pl/e99aad315a2a Andrea A reduced example of where it goes wrong: class B {} struct A { B b = new B; } unittest { A a1, a2; assert(a1 == a2); } In other words, when you initialize the class reference in your struct, it has to be a value that's known at compile-time. So the compiler creates a single instance of B, and every instance of A points to it. So this line: A a = A(A(1), 2); first appends 1 to b.data, then appends 2 to b.data, and it's the same b in both cases. Not knowing what you're attempting to do, I'm not sure how to fix your problem. But if what I've described above does indeed cover it, initializing b in the constructor is the way to get it to work. -- Biotronic
Re: Is it possible to avoid call to destructor for structs?
On Sunday, 24 September 2017 at 18:46:15 UTC, Haridas wrote: Also consider the following code. Please let me know if I am doing the right thing for dynamic arrays. My hack seems to have the desired effect on shutting down the destructor. Is this hack legal use of D? Can you please guide me if/how it can be achieved for std.container.Array? // import std.stdio; struct Bar { ~this() { writeln("~Bar"); } } void main() { { // dynamic array Bar[] bars; bars.length = 4; void* tmp = bars.ptr; delete(tmp); bars.length = 0; } { // std.container.Array import std.container: Array; Array!Bar bars; bars.length = 6; // does not seem to work void* tmp = &(bars[0]); delete(tmp); bars.length = 0; } } Since you're deleting the memory the dynamic array is pointing to, what you're doing is potentially unsafe - if anyone touches that memory after it's been deleted, nasal demons may follow. What you want is something like this: import std.stdio; struct Bar { this(int n) {} ~this() { writeln("~Bar"); } } struct SuppressGC(T) { // Disguise T as a humble array. private ubyte[T.sizeof] _payload; // Create from instance of T. this(T arg) { _payload = *cast(ubyte[T.sizeof]*) } // Or forward constructor arguments to T's constructor. static if (__traits(hasMember, T, "__ctor")) { this(Args...)(Args args) if (__traits(compiles, (Args e){__traits(getMember, T.init, "__ctor")(e);})) { __traits(getMember, get, "__ctor")(args); } } // Pretend to be a T. @property ref T get() { return *cast(T*)_payload.ptr; } alias get this; } void useBar(ref Bar b) {} unittest { // Construct from instance. //This creates a temporary on the stack, and its destructor will be called. SuppressGC!Bar a = Bar(3); // Or by forwarding constructor arguments. // This constructs in-place inside SuppressGC, and no destructor will be called. auto b = SuppressGC!Bar(3); SuppressGC!Bar[] arr; arr.length = 3; // Another stack temporary. Destructor will be called. arr[0] = Bar(5); // No temp arr[1] = SuppressGC!Bar(5); // It even pretends to be the wrapped struct: useBar(b); } In general, of course, this is a bad idea - there's probably a reason that destructor does the thing it's doing. If you're sure skipping it is what you want, go ahead. -- Biotronic
Re: Adding empty static this() causes exception
On Tuesday, 12 September 2017 at 19:59:52 UTC, Joseph wrote: The compiler shouldn't arbitrarily force one to make arbitrary decisions that waste time and money. Like having a type system? Having to do *cast(int*) to interpret a string as an int isn't strictly necessary, and wastes dev time when they have to type extra letters. Throwing an exception when there are import cycles that may cause problems is absolutely the correct thing to do. It's a choice between hard-to-figure-out errors that may depend on the order in which files were passed to the linker, and getting an exception before you've even started running your tests. If we could get this message at compile-time, that would of course be better, but that cannot be done in the general case due to separate compilation. If you want such a message in the cases where it is possible, feel free to create a pull request. It would be possible to add a way to say 'ignore cycles for this module ctor', but as has been pointed out, cycles are generally a symptom of poor architecture, are brittle, and can almost always be avoided (and when they can't, there's a way around that too). -- Biotronic
Re: Adding empty static this() causes exception
The simplest example of a cycle is probably this: module A; import B; int n1 = 17; static this() { n1 = n2; } // module B; import A; int n2 = 42; static this() { n2 = n1; } What's the value of n1 and n2 after module constructors are run? Since both module constructors can run arbitrary code, it's impossible to prove in the general case whether one of them depends on the other. -- Biotronic
Re: Adding empty static this() causes exception
On Tuesday, 12 September 2017 at 09:11:20 UTC, Joseph wrote: I have two nearly duplicate files I added a static this() to initialize some static members of an interface. On one file when I add an empty static this() it crashes while the other one does not. The exception that happens is Cyclic dependency between module A and B. Why does this occur on an empty static this? Is it being ran twice or something? Anyway to fix this? Seriously, simply adding static this() { } to module B crashes the program ;/ module A and module B both import each other because there are types that they need to share but that is all(one uses an enum of the other and vice versa). https://dlang.org/spec/module.html#order_of_static_ctor "Cycles (circular dependencies) in the import declarations are allowed as long as not both of the modules contain static constructors or static destructors. Violation of this rule will result in a runtime exception." So if you have a static this() in both A and B, and A imports B and B imports A, you will get this error message. You can pass --DRT-oncycle=ignore to the program to hide the problem, but a better solution is to find a way to live with fewer static this()s. The reason this exception is thrown is that one module's static this() might otherwise depend on another module's static this that depends on the first module again, and there is no good way to check if it actually depends or just potentially. -- Biotronic
Re: Anonymous nogc class
On Friday, 8 September 2017 at 12:32:35 UTC, Jiyan wrote: On Friday, 8 September 2017 at 06:37:54 UTC, Biotronic wrote: On Thursday, 7 September 2017 at 23:40:11 UTC, Jiyan wrote: [...] Sadly, even std.typecons.scoped isn't currently @nogc: https://issues.dlang.org/show_bug.cgi?id=13972 https://issues.dlang.org/show_bug.cgi?id=17592 [...] First thanks :) i understand the part with scopedAnon, but can you explain what the ~this is referring to? Is it the Modul destructor? It's scoped!T's destructor. If you decide to use that workaround, you should probably copy scoped!T from std.typecons and have your own version. It's not safe in all cases, and the next standard library update might break it. And in general is it just that scoped is just not marked @nogc or is it that it would really need to use the gc? In your case, scoped!T is can be safely called from @nogc code, and thus could be marked @nogc. However, when a class C has a destructor that allocates, scoped!C could not be @nogc. So in order to be safe in all cases, it can't be @nogc in the general case. -- Biotronic
Re: Anonymous nogc class
On Thursday, 7 September 2017 at 23:40:11 UTC, Jiyan wrote: Hey, wanted to know whether it is possible to make anonymous nogc classes: interface I { public void ap(); } void exec(I i) { i.ap; } // now execute, but with something like `scope` exec( new class I { int tr = 43; override void ap(){tr.writeln;} }); Thanks :) Sadly, even std.typecons.scoped isn't currently @nogc: https://issues.dlang.org/show_bug.cgi?id=13972 https://issues.dlang.org/show_bug.cgi?id=17592 This can be worked around by casting scoped's destructor to be @nogc, but that's a heavy-handed approach that ruins type safety, and is the wrong solution in non-@nogc situations. Should you want to, this is what it should look like: ~this() { (cast(void delegate(T) @nogc)((T t){ // `destroy` will also write .init but we have no functions in druntime // for deterministic finalization and memory releasing for now. .destroy(t); }))(Scoped_payload); } If and when this issue is resolved, this should work: interface I { public void ap(); } void exec(I i) { i.ap; } auto scopedAnon(T)(lazy T dummy) if (is(T == class)) { import std.typecons; return scoped!T(); } unittest { auto i = scopedAnon(new class I { int tr = 43; override void ap() { import std.stdio; tr.writeln; } }); exec(i); } -- Biotronic
Re: New programming paradigm
On Thursday, 7 September 2017 at 16:55:02 UTC, EntangledQuanta wrote: Sorry, I think you missed the point completely... or I didn't explain things very well. I don't think I did - your new explanation didn't change my understanding at least. This indicates I'm the one who's bad at explaining. Ah well. The point of my post was mostly to rewrite the code you'd posted in a form that I (and, I hope, others) found easier to understand. I see no where in your code where you have a variant like type. True. I've now rewritten it to use std.variant.Algebraic with these semantics: auto foo(T1, T2)(T1 a, T2 b, int n) { import std.conv; return T1.stringof~": "~to!string(a)~" - "~T2.stringof~": "~to!string(b); } unittest { import std.variant; Algebraic!(float, int) a = 4f; Algebraic!(double, byte) b = 1.23; auto res = varCall!foo(a, b, 3); assert(res == "float: 4 - double: 1.23"); } template varCall(alias fn) { import std.variant; auto varCall(int n = 0, Args...)(Args args) { static if (n == Args.length) { return fn(args); } else { auto arg = args[n]; static if (is(typeof(arg) == VariantN!U, U...)) { foreach (T; arg.AllowedTypes) { if (arg.type == typeid(T)) return varCall!(n+1)(args[0..n], arg.get!T, args[n+1..$]); } assert(false); } else { return varCall!(n+1)(args); } } } } Sadly, by using std.variant, I've given up on the elegant switch/case in exchange for a linear search by typeid. This can be fixed, but requires changes in std.variant. Of course, it would be possible to hide all this behind compiler magic. Is that desirable? I frankly do not think so. We should be wary of adding too much magic to the compiler - it complicates the language and its implementation. This is little more than an optimization, and while a compiler solution would be less intrusive and perhaps more elegant, I do not feel it provides enough added value to warrant its inclusion. Next, I'm curious about this code: void bar(var t) { writeln("\tbar: Type = ", t.type, ", Value = ", t); } void main() { bar(3); // calls bar as if bar was `void bar(int)` bar(3.4f); // calls bar as if bar was `void bar(float)` bar("sad"); // calls bar as if bar was `void bar(string)` } What does 'var' add here, that regular templates do not? (serious question, I'm not trying to shoot down your idea, only to better understand it) One possible problem with var here (if I understand it correctly) would be separate compilation - a generated switch would need to know about types in other source files that may not be available at the time it is compiled. Next: var foo(var x) { if (x == 3) return x; return "error!"; } This looks like a sort of reverse alias this, which I've argued for on many occasions. Currently, it is impossible to implement a type var as in that function - the conversion from string to var would fail. A means of implementing this has been discussed since at least 2007, and I wrote a DIP[1] about it way back in 2013. It would make working with variants and many other types much more pleasant. [1]: https://wiki.dlang.org/DIP52
Re: New programming paradigm
On Wednesday, 6 September 2017 at 23:20:41 UTC, EntangledQuanta wrote: So, no body thinks this is a useful idea or is it that no one understands what I'm talking about? Frankly, you'd written a lot of fairly dense code, so understanding exactly what it was doing took a while. So I sat down and rewrote it in what I'd consider more idiomatic D, partly to better understand what it was doing, partly to facilitate discussion of your ideas. The usage section of your code boils down to this: alias EnumA = TypeMap!(float, int); alias EnumB = TypeMap!(double, byte); auto foo(T1, T2)(T1 a, T2 b) { import std.conv; return T1.stringof~": "~to!string(a)~" - "~T2.stringof~": "~to!string(b); } unittest { int a = 4; double b = 1.23; EnumA enumAVal = EnumA.get!float; EnumB enumBVal = EnumB.get!byte; auto res = enumMapper!(foo, enumAVal, enumBVal)(a, b); assert(res == "float: 4 - byte: 1"); } With this implementation behind the scenes: struct TypeMap(T...) { import std.meta : staticIndexOf; private int value; alias value this; alias Types = T; static TypeMap get(T2)() if (staticIndexOf!(T2, T) > -1) { return TypeMap(staticIndexOf!(T2, T)); } } template enumMapper(alias fn, Maps...) { auto enumMapper(Args...)(Args args) { return enumMapperImpl!(OpaqueAliasSeq!(), Args)(args); } auto enumMapperImpl(alias ArgTypes, Args...)(Args args) { alias Assigned = ArgTypes.Aliases; alias Remaining = Maps[Assigned.length..$]; static if (Remaining.length == 0) { import std.traits : Parameters; alias fun = fn!Assigned; alias params = Parameters!fun; return fun(castTuple!params(args).expand); } else { alias typemap = Remaining[0]; switch (typemap) { foreach (T; typemap.Types) { case typemap.get!T: alias Types = OpaqueAliasSeq!(Assigned, T); return enumMapperImpl!Types(args); } default: assert(false); } } } } template castTuple(T...) { import std.typecons : tuple; auto castTuple(Args...)(Args args) if (Args.length == T.length) { static if (T.length == 0) { return tuple(); } else { auto result = .castTuple!(T[1..$])(args[1..$]); return tuple(cast(T[0])args[0], result.expand); } } } template OpaqueAliasSeq(T...) { alias Aliases = T; }
Re: Template substitution for function parameters
On Saturday, 2 September 2017 at 01:41:14 UTC, Nicholas Wilson wrote: On Friday, 1 September 2017 at 11:33:15 UTC, Biotronic wrote: On Friday, 1 September 2017 at 10:15:09 UTC, Nicholas Wilson wrote: So I have the following types struct DevicePointer(T) { T* ptr; } struct Buffer(T) { void* driverObject; T[] hostMemory; } and a function auto enqueue(alias k)(HostArgsOf!k) { ... } where k would be a function like void foo( DevicePointer!float a, float b , int c) { ... } How can I write HostArgsOf such that HostArgsOf!foo yields: AliasSeq!(Buffer!float, float, int) preferably in such a way that I can add additional transformations to it later on? i.e. it substitutes the template DevicePointer for the template Buffer in Parameters!foo, The templates can be assumed to not be nested templates, i.e. DevicePointer!(DevicePointer!(float)) will never occur neither will Buffer!(Buffer!(float) or any cross templates) template k(alias fn) { import std.meta, std.traits; alias k = staticMap!(ReplaceTemplate!(DevicePointer, Buffer), Parameters!fn); } template ReplaceTemplate(alias needle, alias replacement) { template ReplaceTemplate(alias T) { static if (is(T : needle!Args, Args...)) { alias ReplaceTemplate = replacement!Args; } else { alias ReplaceTemplate = T; } } } Hmm, it seems I oversimplified the example a bit and this doesn't quite work for my actual usecase. struct DevicePointer(int n,T) { T* ptr; } alias GlobalPointer(T) = DevicePointer!(1,T); k!foo yields DevicePointer!(cast(AddrSpace)1u, float), float, int instead of Buffer!float, float, int I think because the is(T : needle!Args, Args...) fails. It really shouldn't work at all - the is(T : ...) works great, but gives Args as (1, float), and fails to instantiate Buffer with those arguments. This should give a compilation error. Anyways, updated code that should work: template ReplaceTemplate(alias needle, alias replacement) { template ReplaceTemplate(alias T) { static if (is(T : needle!Args, Args...)) { alias ReplaceTemplate = replacement!(Args[1]); } else { alias ReplaceTemplate = T; } } } If you only ever use this for DevicePointer and Buffer, a less generic solution might be more understandable for maintainers: template ReplaceDevicePointer(alias T) { static if (is(T : DevicePointer!(n, T), int n, T)) { alias ReplaceDevicePointer = Buffer!T; } else { alias ReplaceDevicePointer = T; } } -- Biotronic
Re: 24-bit int
On Saturday, 2 September 2017 at 00:43:00 UTC, Nicholas Wilson wrote: On Friday, 1 September 2017 at 22:10:43 UTC, Biotronic wrote: struct int24 { ubyte[3] _payload; this(int x) { value = x; } ... } You may also want to put an align(1) on it so that you dont waste 25% of the allocated memory in an array of int24's The very first test in my code checks this: int24[3] a; assert(a.sizeof == 9); On the other hand, using Mir's well-tested code instead of something I hacked together in 10 minutes is probably a good idea. -- Biotronic
Re: get parameter names
On Friday, 1 September 2017 at 20:58:20 UTC, EntangledQuanta wrote: template(A, B...) { auto foo(C...)(C c) { ... get c's parameter names, should be alpha, beta } } foo!(., .)(alpha, beta) I need the actual identifiers passed to foo. I can get the types(obviously C) but when I try to get the identifier names(__traits(identifier or other methods) I stuff get _param_k or errors. I need both C's types and the parameter identifier names past, else I'd just pass as strings. Like Jonathan M Davis points out, this is impossible for regular parameters. For template alias parameters, on the other hand, this works: void bar(alias fn)() { assert(fn.stringof == "alpha"); } unittest { int alpha; bar!(alpha); } -- Biotronic
Re: 24-bit int
On Friday, 1 September 2017 at 19:39:14 UTC, EntangledQuanta wrote: Is there a way to create a 24-bit int? One that for all practical purposes acts as such? This is for 24-bit stuff like audio. It would respect endianness, allow for arrays int24[] that work properly, etc. I haven't looked at endianness beyond it working on my computer. If you have special needs in that regard, consider this a starting point: struct int24 { ubyte[3] _payload; this(int x) { value = x; } @property int value() { int val = *cast(int*)&_payload & 0xFF; if (val & 0x80) { val |= 0xFF00; } return val; } @property int value(int x) { _payload = (cast(ubyte*))[0..3]; return value; } auto opUnary(string op)() { static if (op == "++") { value = value + 1; } else static if (op == "--") { value = value - 1; } else static if (op == "+") { return value; } else static if (op == "-") { return -value; } else static if (op == "~") { return ~value; } else { static assert(false, "Unary operator '"~op~"' is not supported by int24."); } } auto opOpAssign(string op)(int x) { static assert(__traits(compiles, {mixin("value = value "~op~" x;");}), "Binary operator '"~op~"' is not supported by int24."); mixin("value = value "~op~" x;"); return this; } alias value this; } unittest { int24[3] a; assert(a.sizeof == 9); // Max value a[1] = 8388607; assert(a[1] == 8388607); // Test for buffer overflow: assert(a[0] == 0); assert(a[2] == 0); // Overflow a[1] = 8388608; assert(a[1] == -8388608); // Test for buffer overflow: assert(a[0] == 0); assert(a[2] == 0); // negative value a[1] = -1; assert(a[1] == -1); // Test for buffer overflow: assert(a[0] == 0); assert(a[2] == 0); // Unary operators a[1] = 0; assert(~a[1] == -1); a[1]--; assert(a[1] == -1); assert(-a[1] == 1); assert(+a[1] == -1); a[1]++; assert(a[1] == 0); // Binary operators a[1] = 0; a[1] = a[1] + 1; assert(a[1] == 1); a[1] += 1; assert(a[1] == 2); a[1] = a[1] - 1; assert(a[1] == 1); a[1] -= 1; assert(a[1] == 0); a[1] = 3; a[1] = a[1] * 2; assert(a[1] == 6); a[1] = a[1] / 2; assert(a[1] == 3); a[1] *= 2; assert(a[1] == 6); a[1] /= 2; assert(a[1] == 3); a[1] = a[1] << 1; assert(a[1] == 6); a[1] <<= 1; assert(a[1] == 12); a[1] = a[1] >> 1; assert(a[1] == 6); a[1] >>= 1; assert(a[1] == 3); a[1] |= 4; assert(a[1] == 7); a[1] &= 5; assert(a[1] == 5); a[1] = a[1] | 2; assert(a[1] == 7); a[1] = a[1] & 3; assert(a[1] == 3); } -- Biotronic
Re: Template substitution for function parameters
On Friday, 1 September 2017 at 10:15:09 UTC, Nicholas Wilson wrote: So I have the following types struct DevicePointer(T) { T* ptr; } struct Buffer(T) { void* driverObject; T[] hostMemory; } and a function auto enqueue(alias k)(HostArgsOf!k) { ... } where k would be a function like void foo( DevicePointer!float a, float b , int c) { ... } How can I write HostArgsOf such that HostArgsOf!foo yields: AliasSeq!(Buffer!float, float, int) preferably in such a way that I can add additional transformations to it later on? i.e. it substitutes the template DevicePointer for the template Buffer in Parameters!foo, The templates can be assumed to not be nested templates, i.e. DevicePointer!(DevicePointer!(float)) will never occur neither will Buffer!(Buffer!(float) or any cross templates) template k(alias fn) { import std.meta, std.traits; alias k = staticMap!(ReplaceTemplate!(DevicePointer, Buffer), Parameters!fn); } template ReplaceTemplate(alias needle, alias replacement) { template ReplaceTemplate(alias T) { static if (is(T : needle!Args, Args...)) { alias ReplaceTemplate = replacement!Args; } else { alias ReplaceTemplate = T; } } }
Re: Bug in D!!!
On Thursday, 31 August 2017 at 15:48:12 UTC, EntangledQuanta wrote: On Thursday, 31 August 2017 at 10:34:14 UTC, Kagamin wrote: On Thursday, 31 August 2017 at 00:49:22 UTC, EntangledQuanta wrote: I've already implemented a half ass library solution. It can be improved alot. Then, by all means, genius! Enjoy! mixin template virtualTemplates(alias parent, alias fn, T...) { import std.meta; alias name = Alias!(__traits(identifier, fn)[1..$]); mixin virtualTemplates!(parent, name, fn, T); } mixin template virtualTemplates(alias parent, string name, alias fn, T...) { import std.traits; static if (is(parent == interface)) { template templateOverloads(string name : name) { alias templateOverloads = T; } alias Types = T; } else { alias Types = templateOverloads!name; } mixin(virtualTemplatesImpl(name, Types.length, is(parent == class))); } string virtualTemplatesImpl(string name, int n, bool implement) { import std.format; string result; foreach (i; 0..n) { auto body = implement ? format(" { return fn!(Types[%s])(args); }", i) : ";"; result ~= format("ReturnType!(fn!(Types[%s])) %s(Parameters!(fn!(Types[%s])) args)%s\n", i, name, i, body); } return result; } interface I { void _Go(T)(T s); void _Leave(T)(T s); mixin virtualTemplates!(I, _Go, int, short, float, double); mixin virtualTemplates!(I, "Abscond", _Leave, int, short, float, double); } class C : I { void _Go(T)(T s) { } void _Leave(T)(T s) { } mixin virtualTemplates!(C, _Go); mixin virtualTemplates!(C, "Abscond", _Leave); } unittest { I c = new C(); c.Go(3.2); c.Abscond(3.4f); } Does not support multiple template parameters, or template value parameters. Use at own risk for any and all purposes.
Re: Missing array element
On Wednesday, 30 August 2017 at 06:16:16 UTC, Vino.B wrote: On Tuesday, 29 August 2017 at 18:39:03 UTC, Ali Çehreli wrote: https://dlang.org/phobos/std_algorithm_setops.html#.setDifference I tried the setDifference but does seem to be working as expected From the documentation of setDifference: "The two ranges are assumed to be sorted by less." In other words, you will need to sort your ranges first. Either pre-sort them as string[] a = ["test1", "test2", "test3", "test4"]; string[] b = ["test2", "test4"]'; or call setDifference(a.sort(), b.sort()). -- Biotronic
Re: If structures places data to stack why we do not getting stackoverflow on array of structures?
On Wednesday, 16 August 2017 at 12:50:07 UTC, Suliman wrote: MyStruct[] is actually a struct similar to this: struct MyStruct[] { MyStruct* ptr; size_t length; } That struct is placed on the stack, but the data it points to, via the ptr field, is heap allocated. What is struct? Just name and size? I'm sorry, I don't understand what you're asking. Can you please repeat with more information? -- Biotronic
Re: If structures places data to stack why we do not getting stackoverflow on array of structures?
On Wednesday, 16 August 2017 at 07:39:01 UTC, Suliman wrote: On the heap, unless you are allocating it via e.g. alloca. If struct MyStruct { int x; int y; } MyStruct mystruct; is located on stack, why: MyStruct [] mystructs; should located on heap? MyStruct[] is actually a struct similar to this: struct MyStruct[] { MyStruct* ptr; size_t length; } That struct is placed on the stack, but the data it points to, via the ptr field, is heap allocated. One explanation of why is that the compiler doesn't know how many elements are in the array, and that that number may change. If it was stack-allocated and a new element was added to the array, everything on the stack would have to be moved. If the compiler does know the number of elements, it can allocate the array on the stack (theoretically, this could be done as an optimization, but in practice I don't think it is). You can give the compiler this information: MyStruct[10] mystructs; This will allocate 10 MyStructs (80 bytes) on the stack, and if you change 10 to a large number, will give a stack overflow. -- Biotronic
Re: Why does stringof not like functions with arguments?
On Wednesday, 9 August 2017 at 01:39:07 UTC, Jason Brady wrote: Why does the following code error out with: app.d(12,10): Error: function app.FunctionWithArguments (uint i) is not callable using argument types () Like Olivier said, stringof expects a valid expression. There are a few other options: module foo; import std.traits; pragma(msg, bar(3).stringof); // prints "bar(3)" pragma(msg, ().stringof); // "& bar" pragma(msg, fullyQualifiedName!bar);// "foo.bar" pragma(msg, __traits(identifier, bar)); // "bar" void bar(int n) {}
Re: Function with static array as parameter
On Wednesday, 12 July 2017 at 12:20:11 UTC, Miguel L wrote: What is the best way in D to create a function that receives a static array of any length? You will need to use templates: void foo(size_t N)(int[N] arr) { } -- Biotronic
Re: Foreign threads in D code.
On Wednesday, 12 July 2017 at 12:08:35 UTC, Jacob Carlborg wrote: On 2017-07-12 11:28, Biotronic wrote: That's basically what I tried to say It wasn't very clear to me at least. Yeah, I see it in retrospect. "might collect memory that the thread is referencing on the stack or in non-GC memory" doesn't convey that the collected memory is only that which is allocated by the GC. -- Biotronic
Re: Struct Constructor Lazy
On Wednesday, 12 July 2017 at 12:02:37 UTC, Jiyan wrote: Thank you, one last question: If i declare the parameter as ref i, then there shouldnt be any overhead wouldnt it? Thanks :) That would be basically the exact equivalent - instead of passing an int, you'll be passing a pointer. -- Biotronic
Re: Struct Constructor Lazy
On Wednesday, 12 July 2017 at 11:34:45 UTC, Jiyan wrote: Hey, yes i did but to be honest i used dmd in debug version. The thing about the static one, is that it creates a local object A isnt that a performance issue itself - or am i wrong - im confused actually :P? Debug = no optimization. Looking at the generated assembly in a debug build is worthless. You raise a valid point. In a debug build, you're probably right - it will need to copy the temporary to the target. With optimizations enabled, NRVO[0] will populate the target directly, resulting in roughly the equivalent of this code: struct A { int field; static void opCall(A* p) { p.field = getDataFromFile("file.txt"); } } A a; A(); If the function is inlined, the whole problem is of course moot. There are probably other optimizations that can interfere with what I've described. -- Biotronic [0]: https://en.wikipedia.org/wiki/Return_value_optimization
Re: Struct Constructor Lazy
On Wednesday, 12 July 2017 at 11:00:54 UTC, Jiyan wrote: Hey there:) i want to know whether the following is somehow possible: structs dont have default constructors, i know so: struct A { int field; this(int i){field = getDataFromFile("file.txt");} } A instance = A(0); Here comes my issue: when A(0) is called I would want here optimal performance, so there doesnt even need to be a value pushed on the stack (i=0), what would be like having a constructor with zero arguments (i is never used!). Im pretty new to D, can somebody tell me how i would do this? Is this(lazy int i){ ... a solution? The traditional solution is static opCall: struct A { int field; static A opCall() { A result; result.field = getDataFromFile("file.txt"); return result; } } A instance = A(); I believe I've heard this is frowned upon these days, but I don't know of a better solution. For optimal speed you might also want to skip default initialization of result, by writing A result = void;. I would be surprised if the optimizer wasn't able to optimize away the useless parameter though - have you looked at the generated assembly? -- Biotronic
Re: Foreign threads in D code.
On Wednesday, 12 July 2017 at 09:10:07 UTC, Jacob Carlborg wrote: On 2017-07-11 08:18, Biotronic wrote: If DRuntime is not made aware of the thread's existence, the thread will not be stopped by the GC, and the GC might collect memory that the thread is referencing on the stack or in non-GC memory. Are you sure? Wouldn't that make malloc or any other custom allocators completely useless and the D GC would completely break the C standard library because it could collect memory allocated by the C standard library? From "How Garbage Collection Works": "5. Freeing all **GC** allocated memory that has no active pointers to it and do not need destructors to run" [1]. I added the emphasize on "GC". From "Interfacing Garbage Collected Objects With Foreign Code" "If the only pointer to an object is held outside of these areas, then the collector will miss it and free the memory. To avoid this from happening, either * reallocate and copy the object using the foreign code's storage allocator or using the C runtime library's malloc/free." [2]. [1] http://dlang.org/spec/garbage.html#how_gc_works [2] http://dlang.org/spec/garbage.html#gc_foreign_obj That's basically what I tried to say - the GC may collect memory *it has allocated* if the only pointers to it are in memory the GC doesn't scan (i.e. on the stack of an unregistered thread or in memory not allocated via the GC). It will not collect memory allocated by other means, but that Foo* you got from D and are using in C++ might point to a Bar soon after the GC runs. -- Biotronic
Re: Foreign threads in D code.
On Monday, 10 July 2017 at 20:03:32 UTC, Igor Shirkalin wrote: Hello! I have written some D code that I need to link to :C++ huge project. Let it be just one function that uses GC. The question is: if C++ code creates several threads and runs this :D function simultaneously, will GC work correctly? p.s. Of course the druntime is initialized before it. Igor Shirkalin If DRuntime is not made aware of the thread's existence, the thread will not be stopped by the GC, and the GC might collect memory that the thread is referencing on the stack or in non-GC memory. Anything allocated by the GC would still be scanned. To inform DRuntime about your thread, you should call thread_attachThis: https://dlang.org/phobos/core_thread.html#.thread_attachThis As pointed out in the documentation of thread_attachThis, you might also want to call rt_moduleTlsCtor, to run thread local static constructors. Depending on your usage, this might not be necessary. -- Biotronic
Re: Implementing interfaces using alias this
On Thursday, 15 June 2017 at 18:49:58 UTC, Jesse Phillips wrote: wrap!IDuck Ah, so it does exist in Phobos. I thought it should be there, but didn't find it. Thanks! -- Biotronic
Re: Implementing interfaces using alias this
On Wednesday, 14 June 2017 at 09:34:27 UTC, Balagopal Komarath wrote: void main() { Test!Duck d; } As has been pointed out at length by others here, it's simply not how alias this is intended to work. I do see some arguments in favor of working that way, but I'm not sure what's the right solution. Anyways, there is another solution - we could write a template that does the conversion for us. There is std.typecons.Proxy, which seems like a good fit, however it doesn't work quite the way we want. Here however, is a solution that works for simple examples. It might work for more complex examples as well, but I simply haven't tested that: template duckImpl(alias a, TI, Fns...) { static if (Fns.length > 0) { mixin duckImpl!(a, TI, Fns[1..$]); alias thisFn = Fns[0]; enum string fnName = __traits(identifier, thisFn); mixin("ReturnType!thisFn "~fnName~"(Parameters!thisFn args) { return a."~fnName~"(args); }"); } } template duck(TI) if (is(TI == interface)) { TI duck(T)(T t) { import std.meta; import std.traits; template Functions(string s) { alias Functions = MemberFunctionsTuple!(TI, s); } static class Result : TI { private T payload; this(T value) { payload = value; } mixin duckImpl!(payload, TI, staticMap!(Functions, __traits(allMembers, TI))); } return new Result(t); } } interface I { void bar(); } struct S { void bar() { import std.stdio; writeln("OHAI"); } } unittest { I i = S().duck!I; i.bar(); } -- Biotronic
Re: O(1) sum
On Monday, 12 June 2017 at 01:36:04 UTC, Stefan Koch wrote: On Monday, 12 June 2017 at 01:02:58 UTC, helxi wrote: Is it possible to sum an array in O(1)? No. If you want to sum the elements you have to at-least look at all the elements. So it'll always be O(N). it's the best you can do. On a multi-core system we can do better: auto nums = iota(10_000_000.0f); auto sum = taskPool.reduce!"a + b"(nums); Given arbitrary parallelism (yeah, right!), this will be O(log(N)). For real-world systems, it might give a speed-up for large arrays, but won't reduce the big-O complexity. Of course, there will also be overhead to such a solution, so there is a limit to how much one'd actually benefit from it. -- Biotronic
Re: import statement placement
On Wednesday, 7 June 2017 at 12:39:07 UTC, Russel Winder wrote: Are there any idiom rules as to where to put import statements in D? In Python they can go anywhere but PEP-8 suggests they should all go at the top of a file, just after the module documentation string. I don't know if there is any official dogma on how to use imports, but given that local imports inside templates are only loaded if the template is instantiated, there are benefits to using local imports in those cases. Another use for local imports is in unittests - any import that is only used in unittests may benefit from being moved from module scope to inside the unittests themselves, or for an import used by many tests, a version (unittest) block. In my own code, I use very loose guidelines but prefer to put any imports that are used only in one or two functions, inside those functions. Imports that are used by multiple functions are placed at the top of the file, just below the module declaration (if any). Similarly, I try to use selective imports when only a few symbols from a module are used in my module, but find that this becomes unwieldy past 3-5 symbols. -- Biotronic
Re: Can assumeSafeAppend() grab more and more capacity?
On Wednesday, 7 June 2017 at 05:43:06 UTC, ag0aep6g wrote: [snip] It seems to me this is a topic worthy of a more in-depth article. If only I felt up to that. :p When you create a slice 'a' in D (with the current GC and druntime, at least), what happens behind the scenes is the allocator chops off a block of N bytes, where N is some number larger than a.length*typeof(a[0]).sizeof. For an array of two ints, N is 16. For good measure, let's make a copy 'b' of that slice (it will come in handy later): int[] a = [1, 2]; int[] b = a; import std.stdio; writeln(a.capacity); 3 writeln(b.capacity); 3 The capacity is 3. Intriguing, as a block of 16 bytes should be able to hold 4 ints. We can ask the GC for more info on this block: import core.memory; auto info = GC.query(a.ptr); writefln("0x%x, %s, %s ", info.base, info.size, info.attr); 0x2211010, 16, 10 That's the pointer to the start of the block, the size of the block, and various attributes (appendable, e.g.). We can get the raw data of the block: auto block = (cast(ubyte*)info.base)[0..info.size]; writeln(block); [1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8] We can see our 1 and 2 in there, and a curious 8 at the end. That's the currently used data, in bytes. That's also the reason the capacity is 3, not 4 - this info has to live somewhere. If we were to append another element, and print the data again: a ~= 3; writeln(block); [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 12] See how the last byte changed to a 12? That just so happens to be a.length*int.sizeof. Remember how we made a copy of the slice above? The copy's capacity is now 0, while a's capacity is 3. The algorithm for capacity is actually pretty simple: int capacity; if (a.length*int.sizeof == block[$-1]) capacity = (block.length - 1) / int.sizeof; else capacity = 0; writeln(capacity); 3 What happens when we call assumeSafeAppend? b.assumeSafeAppend; writeln(block); [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 8] Hey, the 'used' byte is 8 again. That means a.capacity is 0, while b.capacity is 3. Now for a curious thing: what happens to a's capacity when we append to b? b ~= 4; writeln(a.capacity); 3 As above, the length of a in bytes equals the used bytes in the allocated memory block, and so both a and b have capacity again. This has of course overwritten a[2], which used to be 3 and is now 4. assumeSafeAppend breaks part of D's type system for optimization purposes, and this is the result. Note that the details in this post are only correct for small blocks (<=256 bytes). For larger blocks, the 'used' field is larger, but the algorithms and concepts are the same. For the actual implementation of a.capacity, you can have a look-see at https://github.com/dlang/druntime/blob/master/src/rt/lifetime.d#L734, which is called from https://github.com/dlang/druntime/blob/master/src/object.d#L2968. -- Biotronic
Re: How to cleanup array of structs?
On Friday, 2 June 2017 at 13:32:02 UTC, Suliman wrote: I remember that there was topic about remobing data from struct/arrays of structs. But I do not remember what is idiomatic way to do it, and can't google it. something like: struct MyTrack { ulong id; string recordDate; int velocity; int maxAllowedSpeedForRoad; } MyTrack mytrack; MyTrack [] mytracks; // filling mytracks.clean() or what? There are multiple options // Will set the array to an empty one, and leave the // old one for the GC to clean up when it feels like it. // The safest way. mytracks = null; // Mostly equivalent: mytracks = []; // Will reuse the array, overwriting existing data. // If other parts of the program are using existing data // in the array, this will lead to hard-to-track-down bugs. mytracks.length = 0; mytracks.assumeSafeAppend(); // If you just want to get rid of the last element you added to the array: mytracks = mytracks[0..$-1]; -- Biotronic
Re: purity question
On Tuesday, 30 May 2017 at 13:45:07 UTC, Rene Zwanenburg wrote: On Tuesday, 30 May 2017 at 11:34:52 UTC, ketmar wrote: If malloc were marked as pure, wouldn't that mean it must return the same pointer every time you call it with the same size? of course. but D "pure" is not what other world knows as "pure". we love to mess with words. Well, there's the ability to modify non-const reference parameters from a pure function, but that's not applicable to malloc. Are there any other special rules? The rules[0] are: 0) Can't call functions not marked pure. 1) Can't touch anything mutable that's not explicitly passed to the pure function. 1b) Except GC internal state - i.e. memory can be allocated via the GC. 1c) And floating-point exception flags and modes. 2) Can't do I/O (can be seen as a special case of 1 and 0). There's a few more details, but that's the important stuff. For a good article on the subject, I recommend David Nadlinger's Purity in D: http://klickverbot.at/blog/2012/05/purity-in-d/ [0]: https://dlang.org/spec/function.html#pure-functions
Re: Error: func(const(A) a) is not callable using argument types (const(A)
On Tuesday, 30 May 2017 at 10:46:12 UTC, Andrew Edwards wrote: On Tuesday, 30 May 2017 at 10:37:58 UTC, Biotronic wrote: On Tuesday, 30 May 2017 at 10:31:24 UTC, Daniel Kozak wrote: import std.traits : fqn = fullyQualifiedName; Darnit. I just googled the template and got a result talking about fqn!T. So yeah - this code: import std.traits; pragma(msg, fullyQualifiedName!ImVec2); pragma(msg, fullyQualifiedName!(typeof(CalcTextSize(label.ptr, null, true; -- Biotronic This is exactly the cause. Output is follows: internal.ImVec2 types.ImVec2 I'm leveraging types as I try to do my own port of the lib so CalcTextSize is returning an instance of ImVec2 as defined types and I'm trying to assign to one I have declared in internal. Thanks. Pleasure. :) I don't know why you have two different ImVec2s, but you may have good reasons to. If they need to be separate, you'll need to write a conversion function between the two for the cases where you have one and want the other. This could be the constructor or opAssign, or a standalone function if you want to be more explicit about it. I'd argue that error message could be improved upon, btw. -- Simen
Re: Error: func(const(A) a) is not callable using argument types (const(A)
On Tuesday, 30 May 2017 at 10:31:24 UTC, Daniel Kozak wrote: import std.traits : fqn = fullyQualifiedName; Darnit. I just googled the template and got a result talking about fqn!T. So yeah - this code: import std.traits; pragma(msg, fullyQualifiedName!ImVec2); pragma(msg, fullyQualifiedName!(typeof(CalcTextSize(label.ptr, null, true; -- Biotronic
Re: Error: func(const(A) a) is not callable using argument types (const(A)
On Tuesday, 30 May 2017 at 10:09:50 UTC, Andrew Edwards wrote: What does that even mean? Scenario: bool func(const ImVec2 label_size) { return true; } void main() { //first attempt: const ImVec2 label_size = CalcTextSize(label.ptr, null, true); //Error: cannot implicitly convert expression (CalcTextSize(cast(immutable(char)*)label, null, true, -1F)) of type ImVec2 to const(ImVec2) //second attempt const ImVec2 label_size = cast(const)CalcTextSize(label.ptr, null, true); // Error: cannot implicitly convert expression (CalcTextSize(cast(immutable(char)*)label, null, true, -1F)) of type const(ImVec2) to const(ImVec2) //third attempt const auto label_size = CalcTextSize(label.ptr, null, true); //Okay: don't know why the other two didn't work but I can keep going for now func(label_size); //Error: function imgui_d.func (const(ImVec2) label_size) is not callable using argument types (const(ImVec2)) } My immediate thought is 'is that the same ImVec2?' Do you have two definitions of ImVec2 laying about? What's the output of this code, if you insert it somewhere in the above? import std.traits; pragma(msg, fqn!ImVec2); pragma(msg, fqn!(typeof(CalcTextSize(label.ptr, null, true; -- Biotronic
Re: Code improvement for DNA reverse complement?
On Monday, 22 May 2017 at 08:58:24 UTC, biocyberman wrote: @Nicolas Wilson: Your explanation of the enum is clear and very helpful. I can recall to the same technique used in kh_hash in samtools and the associated. With that said, the chars enum is only to 'T' (85) elements. The reason for having only 85 elements in the array was pure laziness - the problem description seems to forbid non-ACGT letters, so I saw no reason to write more code to handle them. :p My code will crash if the input string contains lower-case letters, Zs, or any other letter beyond 'T' (or read random bits of memory if you're lucky). @ag0aep6g You fell into a trap there. The value is calculated at compile time, but it has >copy/paste-like behavior. That is, whenever you use `chars`, the code behaves as if you >typed out the array literal. That means, the whole array is re-created on every iteration. Use `static immutable` instead. It still forces compile-time calculation, but it doesn't > have copy/paste behavior. Speeds up revComp3 a lot. With 'iteration' here you mean running lifetime of the function, or in other words, each one of the 10_000 cycles in the benchmark? Could you provide some more reading for what you are telling here? I can only guess it is intrinsic behavior of an 'enum'. ag0aep6g is absolutely correct in his observation, and the resulting code is basically this: string revComp3(string bps) { const N = bps.length; static immutable chars_saved = [Repeat!('A'-'\0', '\0'), 'T', Repeat!('C'-'A'-1, '\0'), 'G', Repeat!('G'-'C'-1, '\0'), 'C', Repeat!('T'-'G'-1, '\0'), 'A']; char[] result = new char[N]; for (int i = 0; i < N; ++i) { auto chars = chars_saved.dup; // Bad stuff happens here result[i] = chars[bps[N-i-1]]; } return result.assumeUnique; } As we can see, it copies the entire array for every character in the input string. That's basically an allocation and a memcpy in the innermost, hottest loop. Roughly as bad as it gets. (yup, that's 20 million allocations) As for why this happens, enum can be thought of as the analog of C's #define - the compiler precalculates the data to fill the array, and then copies that into the source code every time it's used.
Re: Code improvement for DNA reverse complement?
On Friday, 19 May 2017 at 22:53:39 UTC, crimaniak wrote: On Friday, 19 May 2017 at 12:55:05 UTC, Biotronic wrote: revComp6 seems to be the fastest, but it's probably also the least readable (a common trade-off). Try revComp7 with -release :) string revComp7(string bps) { char[] result = new char[bps.length]; auto p1 = result.ptr; auto p2 = [$ - 1]; enum AT = 'A'^'T'; enum CG = 'C'^'G'; while (p2 > bps.ptr) { *p1 = *p2 ^ ((*p2 == 'A' || *p2 == 'T') ? AT : CG); p1++; p2--; } return result.assumeUnique; } In fact, when the size of the sequence is growing time difference between procedures is shrinking, so it's much more important to use memory-efficient presentation than to optimize logic. revComp7 is pretty fast, but I followed ag0aep6g's advice: On Friday, 19 May 2017 at 13:38:20 UTC, ag0aep6g wrote: Use `static immutable` instead. It still forces compile-time calculation, but it doesn't have copy/paste behavior. Speeds up revComp3 a lot. Also, with DMD (2.073.0) -release -O instead of -debug from this point. I'd blame someone else, but this is my fault. :p Anyways, full collection of the various versions I've written, plus crimaniak's revComp7 (rebranded as revComp8, because I already had 7 at the time): https://gist.github.com/Biotronic/20daaf0ed1262d313830bc8cd4199896 Timings: revComp0:158 ms, 926 us revComp1: 1 sec, 157 ms, 15 us revComp2:604 ms, 37 us revComp3: 18 ms, 545 us revComp4: 92 ms, 293 us revComp5: 86 ms, 731 us revComp6: 94 ms, 56 us revComp7:917 ms, 576 us revComp8: 62 ms, 917 us This actually matches my expectations - the table lookup version should be crazy fast, and it is. It beats even your revComp7 (revComp8 in the table). LDC (-release -O3) timings: revComp0: 166 ms, 190 us revComp1: 352 ms, 917 us revComp2: 300 ms, 493 us revComp3: 10 ms, 950 us revComp4: 148 ms, 106 us revComp5: 144 ms, 152 us revComp6: 142 ms, 307 us revComp7: 604 ms, 274 us revComp8: 26 ms, 612 us Interesting how revComp4-6 got slower. What I really wanted to see with this though, was the effect on revComp1, which uses ranges all the way.
Re: Code improvement for DNA reverse complement?
On Friday, 19 May 2017 at 12:21:10 UTC, biocyberman wrote: 1. Why do we need to use assumeUnique in 'revComp0' and 'revComp3'? D strings are immutable, so if I'd created the result array as a string, I couldn't change the individual characters. Instead, I create a mutable array, change the elements in it, then cast it to immutable when I'm done. assumeUnique does that casting while keeping other type information and arguably providing better documentation through its name. Behind the scenes, it's basically doing cast(string)result; 2. What is going on with the trick of making chars enum like that in 'revComp3'? By marking a symbol enum, we tell the compiler that its value should be calculated at compile-time. It's a bit of an optimization (but probably doesn't matter at all, and should be done by the compiler anyway), and a way to say it's really, really const. :p Mostly, it's a habit I try to build, of declaring symbols as const as possible, to make maintenance easier. Bonus! Three more variations, all faster than revComp0: string revComp4(string bps) { const N = bps.length; char[] result = new char[N]; for (int i = 0; i < N; ++i) { switch(bps[N-i-1]) { case 'A': result[i] = 'T'; break; case 'C': result[i] = 'G'; break; case 'G': result[i] = 'C'; break; case 'T': result[i] = 'A'; break; default: assert(false); } } return result.assumeUnique; } string revComp5(string bps) { const N = bps.length; char[] result = new char[N]; foreach (i, ref e; result) { switch(bps[N-i-1]) { case 'A': e = 'T'; break; case 'C': e = 'G'; break; case 'G': e = 'C'; break; case 'T': e = 'A'; break; default: assert(false); } } return result.assumeUnique; } string revComp6(string bps) { char[] result = new char[bps.length]; auto p1 = result.ptr; auto p2 = [$-1]; while (p2 > bps.ptr) { switch(*p2) { case 'A': *p1 = 'T'; break; case 'C': *p1 = 'G'; break; case 'G': *p1 = 'C'; break; case 'T': *p1 = 'A'; break; default: assert(false); } p1++; p2--; } return result.assumeUnique; } revComp6 seems to be the fastest, but it's probably also the least readable (a common trade-off).
Re: Code improvement for DNA reverse complement?
On Friday, 19 May 2017 at 07:29:44 UTC, biocyberman wrote: I am solving this problem http://rosalind.info/problems/revc/ as an exercise to learn D. This is my solution: https://dpaste.dzfl.pl/8aa667f962b7 Is there some D tricks I can use to make the `reverseComplement` function more concise and speedy? Any other comments for improvement of the whole solution are also much appreciated. Question about your implementation: you assume the input may contain newlines, but don't handle any other non-ACGT characters. The problem definition states 'DNA string' and the sample dataset contains no non-ACGT chars. Is this an oversight my part or yours, or did you just decide to support more than the problem requires? Here's a few variations I've come up with, and their timings on my machine: import std.exception; import std.stdio; import std.conv; import std.range; import std.algorithm; import std.datetime; import std.meta; string randomDna(int length) { import std.random; auto rnd = Random(unpredictableSeed); enum chars = ['A','C','G','T']; return iota(length).map!(a=>chars[uniform(0,4, rnd)]).array; } unittest { auto input = randomDna(2000); string previous = null; foreach (fn; AliasSeq!(revComp0, revComp1, revComp2, revComp3)) { auto timing = benchmark!({fn(input);})(10_000); writeln(().stringof[2..$], ": ", timing[0].to!Duration); auto current = fn(input); if (previous != null) { if (current != previous) { writeln(().stringof[2..$], " did not give correct results."); } else { previous = current; } } } } // 216 ms, 3 us, and 8 hnsecs string revComp0(string bps) { const N = bps.length; char[] result = new char[N]; for (int i = 0; i < N; ++i) { result[i] = {switch(bps[N-i-1]){ case 'A': return 'T'; case 'C': return 'G'; case 'G': return 'C'; case 'T': return 'A'; default: return '\0'; }}(); } return result.assumeUnique; } // 2 secs, 752 ms, and 969 us string revComp1(string bps) { return bps.retro.map!((a){switch(a){ case 'A': return 'T'; case 'C': return 'G'; case 'G': return 'C'; case 'T': return 'A'; default: assert(false); }}).array; } // 10 secs, 419 ms, 335 us, and 6 hnsecs string revComp2(string bps) { enum chars = ['A': 'T', 'T': 'A', 'C': 'G', 'G': 'C']; auto result = appender!string; foreach_reverse (c; bps) { result.put(chars[c]); } return result.data; } // 1 sec, 972 ms, 915 us, and 9 hnsecs string revComp3(string bps) { const N = bps.length; enum chars = [Repeat!('A'-'\0', '\0'), 'T', Repeat!('C'-'A'-1, '\0'), 'G', Repeat!('G'-'C'-1, '\0'), 'C', Repeat!('T'-'G'-1, '\0'), 'A']; char[] result = new char[N]; for (int i = 0; i < N; ++i) { result[i] = chars[bps[N-i-1]]; } return result.assumeUnique; }
Re: What's a good wat to trunctate a time point
On Friday, 5 May 2017 at 09:14:21 UTC, Biotronic wrote: Here's an implementation that supports start of year, month, week, day, hour, minute and second. Works for DateTime and SysTime. Not heavily tested (all tests included): As the last sentence says, there were holes in the testing, specifically for unsupported units. Updated: import std.datetime; DateTime startOf(string unit, DayOfWeek start = DayOfWeek.sun)(DateTime dt) { static if (unit == "year") { return DateTime(dt.year, 1, 1); } else static if (unit == "month") { return DateTime(dt.year, dt.month, 1); } else static if (unit == "week") { auto delta = dt.dayOfWeek - start; if (delta < 0) delta += 7; return DateTime(dt.year, dt.month, dt.day) - dur!"days"(delta); } else static if (unit == "day") { return DateTime(dt.year, dt.month, dt.day); } else static if (unit == "hour") { return DateTime(dt.year, dt.month, dt.day, dt.hour); } else static if (unit == "minute") { return DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute); } else static if (unit == "second") { return DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second); } else { static assert(false, "\"" ~ unit ~ "\" is not a valid time unit for startOf().\nPlease use one of \"year\", \"month\", \"week\", \"day\", \"hour\", \"minute\" or \"second\""); } } SysTime startOf(string unit)(SysTime st) { return SysTime(startOf!unit(cast(DateTime)st), st.timezone); } unittest { auto now= DateTime(2017, 5, 5, 10, 39, 17); auto expectedYear = DateTime(2017, 1, 1, 0, 0, 0); auto expectedMonth = DateTime(2017, 5, 1, 0, 0, 0); auto expectedWeek = DateTime(2017, 4, 30, 0, 0, 0); auto expectedDay= DateTime(2017, 5, 5, 0, 0, 0); auto expectedHour = DateTime(2017, 5, 5, 10, 0, 0); auto expectedMinute = DateTime(2017, 5, 5, 10, 39, 0); auto expectedSecond = DateTime(2017, 5, 5, 10, 39, 17); auto startOfYear = now.startOf!"year"; auto startOfMonth = now.startOf!"month"; auto startOfWeek = now.startOf!"week"; auto startOfDay= now.startOf!"day"; auto startOfHour = now.startOf!"hour"; auto startOfMinute = now.startOf!"minute"; auto startOfSecond = now.startOf!"second"; assert(expectedYear == startOfYear); assert(expectedMonth == startOfMonth); assert(expectedWeek == startOfWeek); assert(expectedDay== startOfDay); assert(expectedHour == startOfHour); assert(expectedMinute == startOfMinute); assert(expectedSecond == startOfSecond); now = DateTime(2017, 4, 30, 10, 39, 17); auto expectedWeek2 = DateTime(2017, 4, 24, 0, 0, 0); auto startOfWeek2 = now.startOf!("week", DayOfWeek.mon); auto expectedWeek3 = DateTime(2017, 4, 29, 0, 0, 0); auto startOfWeek3 = now.startOf!("week", DayOfWeek.sat); auto expectedWeek4 = DateTime(2017, 4, 30, 0, 0, 0); auto startOfWeek4 = now.startOf!("week", DayOfWeek.sun); assert(startOfWeek2 == expectedWeek2); assert(startOfWeek3 == expectedWeek3); assert(startOfWeek4 == expectedWeek4); assert(!__traits(compiles, now.startOf!"fortnight")); }
Re: What's a good wat to trunctate a time point
On Friday, 5 May 2017 at 08:02:15 UTC, Dukc wrote: I have a time point, be it SysTime or DateTime, whatever. I want to trunctate it to weeks, eg. I want it to become the first point of time during the week it was representing. What's a good way to do that? Only hacks came to my mind. The solution does not have to be generic, trough I'd prefer it to be if it can be without much extra work. Here's an implementation that supports start of year, month, week, day, hour, minute and second. Works for DateTime and SysTime. Not heavily tested (all tests included): import std.datetime; DateTime startOf(string unit, DayOfWeek start = DayOfWeek.sun)(DateTime dt) { static if (unit == "year") { return DateTime(dt.year, 1, 1); } else static if (unit == "month") { return DateTime(dt.year, dt.month, 1); } else static if (unit == "week") { auto delta = dt.dayOfWeek - start; if (delta < 0) delta += 7; return DateTime(dt.year, dt.month, dt.day) - dur!"days"(delta); } else static if (unit == "day") { return DateTime(dt.year, dt.month, dt.day); } else static if (unit == "hour") { return DateTime(dt.year, dt.month, dt.day, dt.hour); } else static if (unit == "minute") { return DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute); } else static if (unit == "second") { return DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second); } } SysTime startOf(string unit)(SysTime st) { return SysTime(startOf!unit(cast(DateTime)st), st.timezone); } unittest { auto now= DateTime(2017, 5, 5, 10, 39, 17); auto expectedYear = DateTime(2017, 1, 1, 0, 0, 0); auto expectedMonth = DateTime(2017, 5, 1, 0, 0, 0); auto expectedWeek = DateTime(2017, 4, 30, 0, 0, 0); auto expectedDay= DateTime(2017, 5, 5, 0, 0, 0); auto expectedHour = DateTime(2017, 5, 5, 10, 0, 0); auto expectedMinute = DateTime(2017, 5, 5, 10, 39, 0); auto expectedSecond = DateTime(2017, 5, 5, 10, 39, 17); auto startOfYear = now.startOf!"year"; auto startOfMonth = now.startOf!"month"; auto startOfWeek = now.startOf!"week"; auto startOfDay= now.startOf!"day"; auto startOfHour = now.startOf!"hour"; auto startOfMinute = now.startOf!"minute"; auto startOfSecond = now.startOf!"second"; assert(expectedYear == startOfYear); assert(expectedMonth == startOfMonth); assert(expectedWeek == startOfWeek); assert(expectedDay== startOfDay); assert(expectedHour == startOfHour); assert(expectedMinute == startOfMinute); assert(expectedSecond == startOfSecond); now = DateTime(2017, 4, 30, 10, 39, 17); auto expectedWeek2 = DateTime(2017, 4, 24, 0, 0, 0); auto startOfWeek2 = now.startOf!("week", DayOfWeek.mon); auto expectedWeek3 = DateTime(2017, 4, 29, 0, 0, 0); auto startOfWeek3 = now.startOf!("week", DayOfWeek.sat); assert(startOfWeek2 == expectedWeek2); assert(startOfWeek3 == expectedWeek3); }
Re: Multiple template alias parameters
On Friday, 8 May 2015 at 21:56:56 UTC, Brian Schott wrote: Allowing template Tem(alias Args ...) syntax would let me trace multiple variables at once. Actually, this already works: void traceVars(alias T, U...)() { import std.stdio : writeln; writeln(T.stringof, : , T); static if (U.length 0) { traceVars!(U); } } void main(string[] args) { auto argslength = args.length; auto args0 = args[0]; traceVars!(args, argslength, args0); } Sadly, the ... syntax precludes the use of __LINE__ and __FILE__. :(