Re: Creating a custom iota()

2022-05-12 Thread realhet via Digitalmars-d-learn

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()

2022-05-12 Thread realhet via Digitalmars-d-learn

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()

2022-05-12 Thread Ali Çehreli via Digitalmars-d-learn

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()

2022-05-12 Thread realhet via Digitalmars-d-learn

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()

2022-05-12 Thread ag0aep6g via Digitalmars-d-learn

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()

2022-05-12 Thread Ali Çehreli via Digitalmars-d-learn

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()

2022-05-12 Thread H. S. Teoh via Digitalmars-d-learn
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()

2022-05-12 Thread realhet via Digitalmars-d-learn

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?