Re: foreach() behavior on ranges

2021-08-24 Thread Ali Çehreli via Digitalmars-d-learn

On 8/24/21 1:44 PM, Ferhat Kurtulmuş wrote:

> Just out of curiosity, if a range implementation uses malloc in save, is
> it only possible to free the memory with the dtor?

Yes but It depends on the specific case. For example, if the type has a 
clear() function that does clean up, then one might call that. I don't 
see it as being different from any other resource management.


> Is a save function only meaningful for GC ranges?

save() is to store the iteration state of a range. It should seldom 
require memory allocation unless we're dealing with e.g. stdin where we 
would have to store input lines just to support save(). It would not be 
a good design to hide such  potentilly expensive storage of lines behind 
save().


To me, save() should mostly be as trivial as returning a copy of the 
struct object to preserve the state of the original range. Here is a 
trivial generator:


import std.range;

struct Squares {
  int current;

  enum empty = false;

  int front() const {
return current * current;
  }

  void popFront() {
++current;
  }

  auto save() {
return this;
  }
}

void main() {
  auto r = Squares(0);
  r.popFront();  // Drop 0 * 0
  r.popFront();  // Drop 1 * 1

  auto copy = r.save;
  copy.popFront();  // Drop 2 * 2 only from the copy

  assert(r.front == 2 * 2);  // Saved original still has 2 * 2
}

Ali




Re: foreach() behavior on ranges

2021-08-24 Thread Steven Schveighoffer via Digitalmars-d-learn

On 8/24/21 2:12 PM, frame wrote:
You can call `popFront` if you need to after the loop, or just before 
the break. I have to say, the term "useless" does not even come close 
to describing ranges using foreach in my experience.


I disagree, because foreach() is a language construct and therefore it 
should behave in a logic way. The methods are fine in ranges or if 
something is done manually. But in case of foreach() it's just unexpected.


I can't agree at all. It's totally expected.

If you have a for loop:

```d
int i;
for(i = 0; i < someArr.length; ++i)
{
   if(someArr[i] == desiredValue) break;
}
```

You are saying, "compiler, please execute the `++i` when I break from 
the loop because I already processed that one". How can that be 
expected? I would *never* expect that. When I break, it means "stop the 
loop, I'm done", and then I use `i` which is where I expected it to be.


It becomes useless for foreach() because you can't rely on them if other 
code breaks the loop and you need to use that range, like in my case. 
But also for ranges - there is no need for a popFront() if it is not 
called in a logic way. Then even empty() could fetch next data if 
needed. It only makes sense if language system code uses it in a 
strictly order and ensures that this order is always assured.


There is no problem with the ordering. What seems to be the issue is 
that you aren't used to the way ranges work.


What's great about D is that there is a solution for you:

```d
struct EagerPopfrontRange(R)
{
   R source;
   ElementType!R front;
   bool empty;
   void popFront() {
 if(source.empty) empty = true;
 else {
front = source.front;
source.popFront;
 }
   }
}

auto epf(R)(R inputRange) {
   auto result = EagerPopfrontRange!R(inputRange);
   result.popFront; // eager!
   return result;
}

// usage
foreach(v; someRange.epf) { ... }
```

Now if you break from the loop, the original range is pointing at the 
element *after* the one you last were processing.



It's not a bug. So there is no need to "handle" it.

The pattern of using a for(each) loop to align certain things occurs 
all the time in code. Imagine a loop that is looking for a certain 
line in a file, and breaks when the line is there. Would you really 
want the compiler to unhelpfully throw away that line for you?


I don't get this point. If it breaks from the loop then it changes the 
scope anyway, so my data should be already processed or copied. What is 
thrown away here?
Why does the loop have to contain all your code? Maybe you have code 
after the loop. Maybe the loop's purpose is to align the range based on 
some criteria (e.g. take this byLine range and prime it so it contains 
the first line of the thing I'm looking for).






And if that is what you want, put `popFront` in the loop before you 
exit. You can't "unpopFront" something, so this provides the most 
flexibility.




Yes, this is the solution but not the way how it should be. If the 
programmer uses the range methods within the foreach-loop then you would 
expect some bug. There shouldn't be a need to manipulate the range just 
because I break the foreach-loop.


You shouldn't need to in most circumstances. I don't think I've ever 
needed to do this. And I use foreach on ranges all the time.


Granted, I probably would use a while loop to align a range rather than 
foreach.




Java, for example just uses next() and hasNext(). You can't run into a 
bug here because one method must move the cursor.


This gives a giant clue as to the problem -- you aren't used to this. 
Java's iterator interface is different than D's. It consumes the element 
as you fetch it, instead of acting like a pointer to a current element. 
Once it gives you the element, it's done with it.


D's ranges are closer to a C++ iterator pair (which is modeled after a 
pair of pointers).


PHP has a rewind() method. So any foreach() would reset the range or 
could clean up before next use of it.


I'm surprised you bring PHP as an example, as it appears their foreach 
interface works EXACTLY as D does:


```php
$arriter = new ArrayIterator(array(1, 2, 3, 4));
foreach($arriter as $val) { if ($val == 2) break; }
print($arriter->current()); // 2
```

But D just lets your range in an inconsistent state between an iteration 
cycle. This feels just wrong. The next foreach() would not continue with 
popFront() but with empty() again - because it even relies on it that a 
range should be called in a given order. As there is no rewind or 
exit-method, this order should be maintained by foreach-exit too, 
preparing for next use. That's it.


You don't see a bug here?



I believe the bug is in your expectations. While Java-like iteration 
would be a possible API D could have chosen, it's not what D chose.


-Steve


Re: foreach() behavior on ranges

2021-08-24 Thread Ferhat Kurtulmuş via Digitalmars-d-learn
On Tuesday, 24 August 2021 at 19:06:44 UTC, Alexandru Ermicioi 
wrote:

On Tuesday, 24 August 2021 at 09:15:23 UTC, bauss wrote:

[...]


Actually the range contracts don't mention that it needs to be 
a by value type. It can also be a reference type, i.e. a class.



[...]


True for any forward range and above, not true for input 
ranges. The problem with them is that some of them are structs, 
and even if they are not forward ranges they do have this 
behavior due to implicit copy on assignment, which can 
potentially make the code confusing.



[...]


If we follow the definition of ranges, they must not be 
copy-able at all. The only way to copy/save, would be to have 
.save method and call that method. This again is not being 
properly followed by even phobos implementations.


Note, that a better approach would be to replace .save in 
definition of forward range with a copy constructor, then all 
non-compliant ranges would become suddenly compliant, while 
those that have .save method should be refactored to a copy 
constructor version.



[...]


You should add .save on assignment if range is a forward range, 
or just remove the assignment if it is not.


Best regards,
Alexandru.


Just out of curiosity, if a range implementation uses malloc in 
save, is it only possible to free the memory with the dtor? I 
worry about that especially when using those nogc range 
implementations with standard library. I don't have a list of the 
functions calling save in phobos. Is a save function only 
meaningful for GC ranges?


Re: foreach() behavior on ranges

2021-08-24 Thread Alexandru Ermicioi via Digitalmars-d-learn

On Tuesday, 24 August 2021 at 09:15:23 UTC, bauss wrote:


A range should be a struct always and thus its state is copied 
when the foreach loop is created.


Actually the range contracts don't mention that it needs to be a 
by value type. It can also be a reference type, i.e. a class.




Which means the state resets every time the loop is initiated.


True for any forward range and above, not true for input ranges. 
The problem with them is that some of them are structs, and even 
if they are not forward ranges they do have this behavior due to 
implicit copy on assignment, which can potentially make the code 
confusing.


If your range uses some internal state that isn't able to be 
copied then or your ranges are not structs then your ranges are 
inherently incorrect.


If we follow the definition of ranges, they must not be copy-able 
at all. The only way to copy/save, would be to have .save method 
and call that method. This again is not being properly followed 
by even phobos implementations.


Note, that a better approach would be to replace .save in 
definition of forward range with a copy constructor, then all 
non-compliant ranges would become suddenly compliant, while those 
that have .save method should be refactored to a copy constructor 
version.




This is what a foreach loop on a range actually compiles to:

```d
for (auto copy = range; !copy.empty; copy.popFront())
{
...
}
```


You should add .save on assignment if range is a forward range, 
or just remove the assignment if it is not.


Best regards,
Alexandru.




Re: foreach() behavior on ranges

2021-08-24 Thread Alexandru Ermicioi via Digitalmars-d-learn

On Tuesday, 24 August 2021 at 08:36:18 UTC, frame wrote:
How do you handle that issue? Are your ranges designed to have 
this bug or do you implement opApply() always?


This is expected behavior imho. I think what you need is a 
forward range, not input range. By the contract of input range, 
it is a consumable object, hence once used in a foreach it can't 
be used anymore. It is similar to an iterator or a stream object 
in java.


Forward range exposes also capability to create save points, 
which is actually used by foreach to do, what it is done in java 
by iterable interface for example.


Then there is bidirectional and random access ranges that offer 
even more capabilities.


Per knowledge I have opApply is from pre range era, and is kinda 
left as an option to provide easy foreach integration. In this 
case you can think of objects having opApply as forward ranges, 
though just for foreach constructs only.


Regards,
Alexandru.


Re: foreach() behavior on ranges

2021-08-24 Thread frame via Digitalmars-d-learn

On Tuesday, 24 August 2021 at 16:45:27 UTC, H. S. Teoh wrote:



In some cases, you *want* to retain the same element between 
loops, e.g., if you're iterating over elements of some category 
and stop when you encounter something that belongs to the next 
category -- you wouldn't want to consume that element, but 
leave it to the next loop to consume it.  So it's not a good 
idea to have break call .popFront automatically.  Similarly, 
sometimes you might want to reuse an element (e.g., the loop 
body detects a condition that warrants retrying).


I'm only talking about foreach() uses and that you should'nt need 
to mix it with manual methods. Such iterations are another topic.




Re: foreach() behavior on ranges

2021-08-24 Thread frame via Digitalmars-d-learn
On Tuesday, 24 August 2021 at 13:02:38 UTC, Steven Schveighoffer 
wrote:

On 8/24/21 4:36 AM, frame wrote:
Consider a simple input range that can be iterated with 
empty(), front() and popFront(). That is comfortable to use 
with foreach() but what if the foreach loop will be cancelled? 
If a range isn't depleted yet and continued it will supply the 
same data twice on front() in the next use of foreach().


For some reason, foreach() does not call popFront() on a break 
or continue statement.


continue calls `popFront`. break does not.


Of course by the next iteration, you are right.

You can call `popFront` if you need to after the loop, or just 
before the break. I have to say, the term "useless" does not 
even come close to describing ranges using foreach in my 
experience.


I disagree, because foreach() is a language construct and 
therefore it should behave in a logic way. The methods are fine 
in ranges or if something is done manually. But in case of 
foreach() it's just unexpected.


It becomes useless for foreach() because you can't rely on them 
if other code breaks the loop and you need to use that range, 
like in my case. But also for ranges - there is no need for a 
popFront() if it is not called in a logic way. Then even empty() 
could fetch next data if needed. It only makes sense if language 
system code uses it in a strictly order and ensures that this 
order is always assured.




It's not a bug. So there is no need to "handle" it.

The pattern of using a for(each) loop to align certain things 
occurs all the time in code. Imagine a loop that is looking for 
a certain line in a file, and breaks when the line is there. 
Would you really want the compiler to unhelpfully throw away 
that line for you?


I don't get this point. If it breaks from the loop then it 
changes the scope anyway, so my data should be already processed 
or copied. What is thrown away here?




And if that is what you want, put `popFront` in the loop before 
you exit. You can't "unpopFront" something, so this provides 
the most flexibility.


-Steve


Yes, this is the solution but not the way how it should be. If 
the programmer uses the range methods within the foreach-loop 
then you would expect some bug. There shouldn't be a need to 
manipulate the range just because I break the foreach-loop.


Java, for example just uses next() and hasNext(). You can't run 
into a bug here because one method must move the cursor.


PHP has a rewind() method. So any foreach() would reset the range 
or could clean up before next use of it.


But D just lets your range in an inconsistent state between an 
iteration cycle. This feels just wrong. The next foreach() would 
not continue with popFront() but with empty() again - because it 
even relies on it that a range should be called in a given order. 
As there is no rewind or exit-method, this order should be 
maintained by foreach-exit too, preparing for next use. That's it.


You don't see a bug here?



Re: foreach() behavior on ranges

2021-08-24 Thread H. S. Teoh via Digitalmars-d-learn
On Tue, Aug 24, 2021 at 08:36:18AM +, frame via Digitalmars-d-learn wrote:
> Consider a simple input range that can be iterated with empty(),
> front() and popFront(). That is comfortable to use with foreach() but
> what if the foreach loop will be cancelled? If a range isn't depleted
> yet and continued it will supply the same data twice on front() in the
> next use of foreach().

Generally, if you need precise control over range state between multiple
loops, you really should think about using a while loop instead of a for
loop, and call .popFront where it's needed.


> For some reason, foreach() does not call popFront() on a break or continue
> statement. There is no way to detect it except the range itself tracks its
> status and does an implicit popFront() if needed - but then this whole
> interface is some kind of useless.

In some cases, you *want* to retain the same element between loops,
e.g., if you're iterating over elements of some category and stop when
you encounter something that belongs to the next category -- you
wouldn't want to consume that element, but leave it to the next loop to
consume it.  So it's not a good idea to have break call .popFront
automatically.  Similarly, sometimes you might want to reuse an element
(e.g., the loop body detects a condition that warrants retrying).

Basically, once you need anything more than a single sequential
iteration over a range, it's better to be explicit about what exactly
you want, rather than depend on implicit semantics, which may lead to
surprising results.

while (!range.empty) {
doSomething(range.front);
if (someCondition) {
range.popFront;
break;
} else if (someOtherCondition) {
// Don't consume current element
break;
} else if (skipElement) {
range.popFront;
continue;
} else if (retryElement) {
continue;
}
range.popFront; // normal iteration
}


T

-- 
"No, John.  I want formats that are actually useful, rather than
over-featured megaliths that address all questions by piling on
ridiculous internal links in forms which are hideously over-complex." --
Simon St. Laurent on xml-dev


Re: C to D convertor

2021-08-24 Thread Виталий Фадеев via Digitalmars-d-learn

On Tuesday, 24 August 2021 at 11:52:45 UTC, Dennis wrote:
On Saturday, 21 August 2021 at 08:14:22 UTC, Виталий Фадеев 
wrote:

Any more ?


CPP2D
https://github.com/lhamot/CPP2D


Dennis, thank!


Re: foreach() behavior on ranges

2021-08-24 Thread Steven Schveighoffer via Digitalmars-d-learn

On 8/24/21 4:36 AM, frame wrote:
Consider a simple input range that can be iterated with empty(), front() 
and popFront(). That is comfortable to use with foreach() but what if 
the foreach loop will be cancelled? If a range isn't depleted yet and 
continued it will supply the same data twice on front() in the next use 
of foreach().


For some reason, foreach() does not call popFront() on a break or 
continue statement. 


continue calls `popFront`. break does not.

There is no way to detect it except the range itself 
tracks its status and does an implicit popFront() if needed - but then 
this whole interface is some kind of useless.


You can call `popFront` if you need to after the loop, or just before 
the break. I have to say, the term "useless" does not even come close to 
describing ranges using foreach in my experience.


There is opApply() on the other hand that is designed for foreach() and 
informs via non-0-result if the loop is cancelled - but this means that 
every range must implement it if the range should work in foreach() 
correctly?


`opApply` has to return different values because it needs you to pass 
through its instructions to the compiler-generated code. The compiler 
has written the delegate to return the message, and so you need to pass 
through that information. The non-zero result is significant, not just 
non-zero. For instance, if you end with a `break somelabel;` statement, 
it has to know which label to go to.


The correct behavior for `opApply` should be, if the delegate returns 
non-zero, return that value immediately. It should not be doing anything 
else. Would you be happy with a `break somelabel;` actually triggering 
output? What if it just continued the loop instead? You don't get to 
decide what happens at that point, you are acting as the compiler.


This is very inconsistent. Either foreach() should deny usage of ranges 
that have no opApply() method or there should be a reset() or cancel() 
method in the interfaces that may be called by foreach() if they are 
implemented.


How do you handle that issue? Are your ranges designed to have this bug 
or do you implement opApply() always?


It's not a bug. So there is no need to "handle" it.

The pattern of using a for(each) loop to align certain things occurs all 
the time in code. Imagine a loop that is looking for a certain line in a 
file, and breaks when the line is there. Would you really want the 
compiler to unhelpfully throw away that line for you?


And if that is what you want, put `popFront` in the loop before you 
exit. You can't "unpopFront" something, so this provides the most 
flexibility.


-Steve


Re: C to D convertor

2021-08-24 Thread Dennis via Digitalmars-d-learn

On Saturday, 21 August 2021 at 08:14:22 UTC, Виталий Фадеев wrote:

Any more ?


CPP2D
https://github.com/lhamot/CPP2D


Re: Profiling

2021-08-24 Thread JG via Digitalmars-d-learn

On Tuesday, 24 August 2021 at 09:45:31 UTC, JG wrote:

On Tuesday, 24 August 2021 at 09:42:29 UTC, JG wrote:

On Tuesday, 24 August 2021 at 09:36:06 UTC, JG wrote:

[...]


In case anyone is interested it seems that it was forked to 
here:


https://github.com/joakim-brannstrom/profdump

Perhaps the dub page could be updated?


Tried to continue following the instructions.
Ran:
profdump -b trace.log trace.log.b
Got:
core.exception.RangeError@source/app.d(134): Range violation

??:? [0x557458c2bad5]
??:? [0x557458c2d556]
??:? [0x557458c147af]
??:? [0x557458c0b458]
??:? [0x557458c0bb47]
app.d:133 [0x557458b96199]
??:? [0x557458c1449b]
??:? [0x557458c14397]
??:? [0x557458c141ed]
/home/jg/dlang/ldc-1.26.0/bin/../import/core/internal/entrypoint.d:42 
[0x557458b979d4]
??:? __libc_start_main [0x7f0f84d39cb1]
??:? [0x557458b4a0cd]


The reason for the crash boils down to the fact that this fails:

foreach(k; sort!"a > b"(funcs.keys)) assert(k in funcs);

funcs is of type ubyte[4][float]

Is this a compiler bug?


Re: foreach() behavior on ranges

2021-08-24 Thread frame via Digitalmars-d-learn

On Tuesday, 24 August 2021 at 09:26:20 UTC, jfondren wrote:

I think you strayed from the beaten path, in a second way, as 
soon as your range's lifetime escaped a single expression, to 
be possibly used in two foreach loops. With ranges, as you do 
more unusual things, you're already encouraged to use a more 
advanced range. And ranges already have caveats for surprising 
behavior, like map/filter interactions that redundantly execute 
code. So I see this as a documentation problem. The current 
behavior of 'if you break then the next foreach gets what you 
broke on' is probably a desirable behavior for some uses:


Yes, I have a special case where a delegate jumps back to the 
range because something must be buffered before it can be 
delivered.



```d
import std;

class MyIntRange {
int[] _elements;
size_t _offset;

this(int[] elems) { _elements = elems; }

bool empty() { return !_elements || _offset >= 
_elements.length; }


int front() { return _elements[_offset]; }

void popFront() { _offset++; }
}

void main() {
auto ns = new MyIntRange([0, 1, 1, 2, 3, 4, 4, 4, 5]);
// calls writeln() as many times as there are numbers:
while (!ns.empty) {
foreach (odd; ns) {
if (odd % 2 == 0) break;
writeln("odd: ", odd);
}
foreach (even; ns) {
if (even % 2 != 0) break;
writeln("even: ", even);
}
}
}
```


That is just weird. It's not logical and a source of bugs. I 
mean, we should use foreach() to avoid loop-bugs. Then it's a 
desired behavior to rely on that?


Re: foreach() behavior on ranges

2021-08-24 Thread frame via Digitalmars-d-learn

On Tuesday, 24 August 2021 at 09:15:23 UTC, bauss wrote:



A range should be a struct always and thus its state is copied 
when the foreach loop is created.


This is not conform with the aggregate expression mentioned in 
the manual where a class object would be also allowed.



Which means the state resets every time the loop is initiated.


Yes, it should reset - thus foreach() also needs to handle that 
correctly.









Re: Profiling

2021-08-24 Thread JG via Digitalmars-d-learn

On Tuesday, 24 August 2021 at 09:42:29 UTC, JG wrote:

On Tuesday, 24 August 2021 at 09:36:06 UTC, JG wrote:

On Wednesday, 10 February 2021 at 23:42:31 UTC, mw wrote:

[...]


I tried to do this, but I am not sure how to install profdump.
What I did is cloned the repository using git.
Tried to build it using dub but got an error as described here:

[...]


In case anyone is interested it seems that it was forked to 
here:


https://github.com/joakim-brannstrom/profdump

Perhaps the dub page could be updated?


Tried to continue following the instructions.
Ran:
profdump -b trace.log trace.log.b
Got:
core.exception.RangeError@source/app.d(134): Range violation

??:? [0x557458c2bad5]
??:? [0x557458c2d556]
??:? [0x557458c147af]
??:? [0x557458c0b458]
??:? [0x557458c0bb47]
app.d:133 [0x557458b96199]
??:? [0x557458c1449b]
??:? [0x557458c14397]
??:? [0x557458c141ed]
/home/jg/dlang/ldc-1.26.0/bin/../import/core/internal/entrypoint.d:42 
[0x557458b979d4]
??:? __libc_start_main [0x7f0f84d39cb1]
??:? [0x557458b4a0cd]


Re: Profiling

2021-08-24 Thread JG via Digitalmars-d-learn

On Tuesday, 24 August 2021 at 09:36:06 UTC, JG wrote:

On Wednesday, 10 February 2021 at 23:42:31 UTC, mw wrote:

[...]


I tried to do this, but I am not sure how to install profdump.
What I did is cloned the repository using git.
Tried to build it using dub but got an error as described here:

[...]


In case anyone is interested it seems that it was forked to here:

https://github.com/joakim-brannstrom/profdump

Perhaps the dub page could be updated?


Re: Profiling

2021-08-24 Thread JG via Digitalmars-d-learn

On Wednesday, 10 February 2021 at 23:42:31 UTC, mw wrote:

On Wednesday, 10 February 2021 at 11:52:51 UTC, JG wrote:


As a follow up question I would like to know what tool people 
use to profile d programs?


I use this one:

https://code.dlang.org/packages/profdump

e.g.

```
dub build --build=debug --build=profile

# run your program to generate trace.log

profdump -b trace.log trace.log.b
profdump -f --dot --threshold 1 trace.log trace.log.dot
echo 'view it with: xdot trace.log.dot'
```


I tried to do this, but I am not sure how to install profdump.
What I did is cloned the repository using git.
Tried to build it using dub but got an error as described here:

https://github.com/AntonMeep/profdump/issues/6

I then modified the dub.json and got it to build but it only
produced a library. So I modified the dub.json again to tell
it to build an executable and got:

core.exception.AssertError@source/app.d(4): TODO

??:? [0x561af7b38025]
??:? [0x561af7b39aa6]
??:? [0x561af7b1cd8f]
??:? [0x561af7b15469]
app.d:4 [0x561af7aebc62]
??:? [0x561af7b1ca7b]
??:? [0x561af7b1c977]
??:? [0x561af7b1c7cd]
/home/james/dlang/ldc-1.26.0/bin/../import/core/internal/entrypoint.d:42 
[0x561af7aebc94]
??:? __libc_start_main [0x7f5ba99accb1]
??:? [0x561af7aeb62d]
Program exited with code 1

I then looked inside source/app.d and found:

version(unittest) {

} else {
void main() { assert(0, "TODO"); }
}


How does one install profdump?





Re: foreach() behavior on ranges

2021-08-24 Thread jfondren via Digitalmars-d-learn

On Tuesday, 24 August 2021 at 08:36:18 UTC, frame wrote:
Consider a simple input range that can be iterated with 
empty(), front() and popFront(). That is comfortable to use 
with foreach() but what if the foreach loop will be cancelled? 
If a range isn't depleted yet and continued it will supply the 
same data twice on front() in the next use of foreach().


I think you strayed from the beaten path, in a second way, as 
soon as your range's lifetime escaped a single expression, to be 
possibly used in two foreach loops. With ranges, as you do more 
unusual things, you're already encouraged to use a more advanced 
range. And ranges already have caveats for surprising behavior, 
like map/filter interactions that redundantly execute code. So I 
see this as a documentation problem. The current behavior of 'if 
you break then the next foreach gets what you broke on' is 
probably a desirable behavior for some uses:


```d
import std;

class MyIntRange {
int[] _elements;
size_t _offset;

this(int[] elems) { _elements = elems; }

bool empty() { return !_elements || _offset >= 
_elements.length; }


int front() { return _elements[_offset]; }

void popFront() { _offset++; }
}

void main() {
auto ns = new MyIntRange([0, 1, 1, 2, 3, 4, 4, 4, 5]);
// calls writeln() as many times as there are numbers:
while (!ns.empty) {
foreach (odd; ns) {
if (odd % 2 == 0) break;
writeln("odd: ", odd);
}
foreach (even; ns) {
if (even % 2 != 0) break;
writeln("even: ", even);
}
}
}
```


Re: foreach() behavior on ranges

2021-08-24 Thread bauss via Digitalmars-d-learn

On Tuesday, 24 August 2021 at 08:36:18 UTC, frame wrote:
Consider a simple input range that can be iterated with 
empty(), front() and popFront(). That is comfortable to use 
with foreach() but what if the foreach loop will be cancelled? 
If a range isn't depleted yet and continued it will supply the 
same data twice on front() in the next use of foreach().


For some reason, foreach() does not call popFront() on a break 
or continue statement. There is no way to detect it except the 
range itself tracks its status and does an implicit popFront() 
if needed - but then this whole interface is some kind of 
useless.


There is opApply() on the other hand that is designed for 
foreach() and informs via non-0-result if the loop is cancelled 
- but this means that every range must implement it if the 
range should work in foreach() correctly?


This is very inconsistent. Either foreach() should deny usage 
of ranges that have no opApply() method or there should be a 
reset() or cancel() method in the interfaces that may be called 
by foreach() if they are implemented.


How do you handle that issue? Are your ranges designed to have 
this bug or do you implement opApply() always?


A range should be a struct always and thus its state is copied 
when the foreach loop is created.


Which means the state resets every time the loop is initiated.

If your range uses some internal state that isn't able to be 
copied then or your ranges are not structs then your ranges are 
inherently incorrect.


This is what a foreach loop on a range actually compiles to:

```d
for (auto copy = range; !copy.empty; copy.popFront())
{
...
}
```

This is easily evident in this example:

https://run.dlang.io/is/YFuWHn

Which prints:
1
2
1
2
3
4
5

Unless I'm misunderstanding your concern?


foreach() behavior on ranges

2021-08-24 Thread frame via Digitalmars-d-learn
Consider a simple input range that can be iterated with empty(), 
front() and popFront(). That is comfortable to use with foreach() 
but what if the foreach loop will be cancelled? If a range isn't 
depleted yet and continued it will supply the same data twice on 
front() in the next use of foreach().


For some reason, foreach() does not call popFront() on a break or 
continue statement. There is no way to detect it except the range 
itself tracks its status and does an implicit popFront() if 
needed - but then this whole interface is some kind of useless.


There is opApply() on the other hand that is designed for 
foreach() and informs via non-0-result if the loop is cancelled - 
but this means that every range must implement it if the range 
should work in foreach() correctly?


This is very inconsistent. Either foreach() should deny usage of 
ranges that have no opApply() method or there should be a reset() 
or cancel() method in the interfaces that may be called by 
foreach() if they are implemented.


How do you handle that issue? Are your ranges designed to have 
this bug or do you implement opApply() always?


Re: Possible to overload assignment of struct field ??

2021-08-24 Thread james.p.leblanc via Digitalmars-d-learn

On Tuesday, 24 August 2021 at 05:34:08 UTC, Ali Çehreli wrote:

On 8/23/21 10:25 PM, james.p.leblanc wrote:

So, you need a "property". Easy... :)

1) Rename the member e.g. as a_.

2) Write setter and getter functions named 'a'.

struct Foo{
  int a_;

  int a() const {
return a_;
  }

  void a(int value) {
a_ = value;
  }
}

void main(){
  auto x = Foo(1);
  x.a = 100;
  assert(x.a == 100);
}

Ali


Ali,

Thank you ... yes!  This is exactly what I needed, I have done 
something
similar as you have shown for the "getter", but had a 
"brain-lock-up"

when thinking about the setter.

A bit embarassing for my, I admit.  But, on the positive side ... 
the

solution is now burned into my brain.

Thanks again and Kind Regards,
James