Re: Creating a custom iota()
On Thursday, 12 May 2022 at 20:12:19 UTC, Ali Çehreli wrote: And I've been thinking 'iota' may not be as suitable as I thought at first. I like the following even more: auto r0 = st .by(Duration(2)) .take(5); So I wrote this by() for my DateTime and then: import quantities.si; auto by(in DateTime begin, in Frequency f){ return begin.by(1/f); } //This let me do: now.by(60*hertz) .until!"a>b"(now+1*second) .each!writeln; My mind is blowing! :D
Re: Creating a custom iota()
On Thursday, 12 May 2022 at 17:06:39 UTC, Ali Çehreli wrote: I don't care whether it is good practice or not. :) The following is what you meant anyway and seems to work. I restricted the parameter types to the ones I wanted to use. And for the standard iota behavior I used a public import. public import std.range : iota; auto iota(in DateTime begin, in DateTime end, in Time step){ //https://forum.dlang.org/post/ivskeghrhbuhpiyte...@forum.dlang.org -> Ali's solution static struct Result{ DateTime current, end; Time step; @property bool empty(){ return current >= end; } @property auto front(){ return current; } void popFront(){ assert(!empty); current += step; } } return Result(begin, end, step); } ... iota(st, en, day).each!writeln; //works iota(1, 10, 0.5).each!writeln; //also works It works perfectly, Thank You very much! Although that general implementation of iota is a bit complex for me, this specialized one is simple. note(0): no cast() was needed here, worked with a const DateTime{ ulong ticks; ... }
Re: Creating a custom iota()
On 5/12/22 12:51, ag0aep6g wrote: >> auto iota(B, E, S)(B begin, E end, S step) > [...] >> { >> static struct Result >> { >> B current; > [...] >> void popFront() >> { > [...] >> current += step; >> } >> } > [...] >> } > > Mark iota's `begin` parameter as const. Then you don't need the cast, > because `B` will be mutable. Cool trick! Like this: auto iota(B, E, S)(const(B) begin, E end, S step) { // ... } It works with non-const values as well. So apparently it makes the function parameter const(B) and deduces B to be the non-const version of that, which always produces the non-const version. And I've been thinking 'iota' may not be as suitable as I thought at first. I like the following even more: auto r0 = st .by(Duration(2)) .take(5); auto r1 = st .by(Duration(2)) .until(en); A family of 'by' ovenloads can be defined or it can be templatized to be used as 'by!Duration(2)' etc.: auto by(DateTime dt, Duration dur) { struct Result { DateTime front; Duration dur; enum empty = false; void popFront() { front += dur; } } return Result(dt, dur); } Just works. But one may want to provide an accessor for front(). Ali
Re: Creating a custom iota()
On Thursday, 12 May 2022 at 16:57:35 UTC, H. S. Teoh wrote: Does your DateTime type support the `++` operator? It can't because I only want to use the quantities.si.Time type to do arithmetic with my DateTime. In my previous DateTime, it was a lot of problem that I was doing math on it's raw internal variable (which was a double where 1.0 meant 1 day.) In the new version adding int(1) does a compilation error. I will use 1*second or 1*day or 1*micro(second) instead. I will always state the measurement unit to avoid confusions. But I'm curious, why didn't you use std.datetime? There are many weird reasons: 5 years ago I moved to DLang from Delphi. Since 20 years I always used the old Borland date system whist stards in 1899, is an ieee double, 1 = 1 day. I only did stuff with local times, it was enough. Recently I had to work with Python as well, and there I started to have problems with time synchronizations between the 2 systems. I ended up sending the exact Borland time to the Python program and make my little routines to work with them exactly like I do 20 years ago in Delphi, 5 years ago in DLang. But I know it is not nice. So a week ago I discovered the quantities package and I decided to rewrite my DateTime to use the Time type in that. It solves all my measurement unit madness in a very elegant way. So I also want those measurement units working in all my systems. (I have lengths, frequencies too) For me std.datetime was not an option because that felt so big I scared of it at the first look. It's 1.5MB, the same size as my complete program I'm working on :D I also hade a second based time measurement 'system', it used QueryPerformanceCounter. I want do discontinue that as well. So these are the features of the new DateTime: - small size: 64 uint is the internal format. (Half of std.systemtime, but it lacks timezone, it's only UTC internally). - precise: 100.0/64 nanosecond is the smallest unit. It covers 913 years. - It was built around the new WinAPI GetSystemTimePreciseAsFileTime() It gives me 100ns resolution UTC time in less than 50ns. So that's why I don't need QueryPerformanceCounter anymore. - The extra 6 bits under the 100ns ticks will be used for time based unique ID generation. It's better for me than having 6 years at my hand. - The integration with quantities.si makes my work much easier. I can't wait to use it in my program, as I knows physics things better than me, haha. std.range.recurrence. Indeed, that's an option as well, I forgot about. Thank You!
Re: Creating a custom iota()
On Thursday, 12 May 2022 at 17:06:39 UTC, Ali Çehreli wrote: void main() { const st = DateTime(Duration(0)); [...] // (0) I think D should not insist on 'const' // when copying types that have no indirections. // We shouldn't need the cast() below in this case. [...] iota(cast()st, en, step).each!writeln; } [...] auto iota(B, E, S)(B begin, E end, S step) [...] { static struct Result { B current; [...] void popFront() { [...] current += step; } } [...] } Mark iota's `begin` parameter as const. Then you don't need the cast, because `B` will be mutable.
Re: Creating a custom iota()
On 5/12/22 04:57, realhet wrote: > //this would be nicer, but not works > iota(st, en, day).each!writeln; For others, the problem is, iota does have a version that works with user types but not one that parameterizes 'step'. An oversight? > My question is, is there a way to 'extend' the functionality of the > std.iota() function or it is better to come up with a unique name for > this special 'iota' functionality? I think ioat can be improved to work with user 'step' types as I did below. > Is this a good practice I don't care whether it is good practice or not. :) The following is what you meant anyway and seems to work. I added 6 comments: import std.stdio; import std.range; import std.algorithm; struct Duration { ulong value; } struct DateTime { Duration sinceEpoch; // Quickest implementation for this post auto opOpAssign(string op)(Duration dur) if (op == "+") { return DateTime(Duration(sinceEpoch.value += dur.value)); } } void main() { const st = DateTime(Duration(0)); const en = DateTime(Duration(10)); const step = Duration(1); // (0) I think D should not insist on 'const' // when copying types that have no indirections. // We shouldn't need the cast() below in this case. // // Alternatively, the implementation of iota() // could use Unqual after detecting B has no // indirections. That would be better for the // user but again, the language should copy // to non-const by-default. But then, I am // sure there would be cases where an unexpected // function overload might be selected in some // cases. iota(cast()st, en, step).each!writeln; } // I adapted the following template from my // /usr/include/dlang/dmd/std/range/package.d // and then: // (1) Added 'S step' auto iota(B, E, S)(B begin, E end, S step) // (2) Removed for now // if (!isIntegral!(CommonType!(B, E)) && // !isFloatingPoint!(CommonType!(B, E)) && // !isPointer!(CommonType!(B, E)) && // is(typeof((ref B b) { ++b; })) && // (is(typeof(B.init < E.init)) || is(typeof(B.init == E.init))) ) { static struct Result { B current; E end; S step; // (3) Added @property bool empty() { static if (is(typeof(B.init < E.init))) return !(current < end); else static if (is(typeof(B.init != E.init))) return current == end; else static assert(0); } @property auto front() { return current; } void popFront() { assert(!empty); // (4) Used += instead of ++current // This can be improved to use the other // method a.l.a. "design by introspection". current += step; } } // (5) Added step return Result(begin, end, step); } Ali
Re: Creating a custom iota()
On Thu, May 12, 2022 at 11:57:54AM +, realhet via Digitalmars-d-learn wrote: [...] > I have my own DateTime struct. > It has opCmp() and opBinary(), I can do arithmetic with this custom > DateTime and the amazing time units of the **quantities** package. > > Now I'm about mo make iterations in a DateTime range: > > const > st = DateTime(UTC, "22.1.1 8:30").utcDayStart, > en = DateTime(UTC, "22.3.5 19:56").max(now).utcDayStart + > (100.0/64)*nano(second); > > //this works > for(auto d = cast()st; d < en; d += day) writeln(d); > > //this would be nicer, but not works > iota(st, en, day).each!writeln; > > My question is, is there a way to 'extend' the functionality of the > std.iota() function or it is better to come up with a unique name for > this special 'iota' functionality? Custom user types should already be supported by iota; see: https://issues.dlang.org/show_bug.cgi?id=10762 Does your DateTime type support the `++` operator? If not, that's the most likely reason iota failed to instantiate on it. Just add `opUnary(string op : "++")` to your type, and it should work. [...] > Or maybe put it inside a DateTimeRange struct, and implement the std > range functionality? [...] That would work too. But I'm curious, why didn't you use std.datetime? The DateTime type there supports inter-date arithmetic (as far as it makes sense), and can be easily made into a range using std.range.recurrence. T -- When solving a problem, take care that you do not become part of the problem.
Creating a custom iota()
Hello, I have my own DateTime struct. It has opCmp() and opBinary(), I can do arithmetic with this custom DateTime and the amazing time units of the **quantities** package. Now I'm about mo make iterations in a DateTime range: const st = DateTime(UTC, "22.1.1 8:30").utcDayStart, en = DateTime(UTC, "22.3.5 19:56").max(now).utcDayStart + (100.0/64)*nano(second); //this works for(auto d = cast()st; d < en; d += day) writeln(d); //this would be nicer, but not works iota(st, en, day).each!writeln; My question is, is there a way to 'extend' the functionality of the std.iota() function or it is better to come up with a unique name for this special 'iota' functionality? I know that if I'm going to patch iota in my module, I have to make ALL the iota() variants plus my special iota variant visible in that module scope. Is this a good practice, or is there a better way? Or maybe put it inside a DateTimeRange struct, and implement the std range functionality?