Re: Adapting foreign iterators to D ranges

2024-04-24 Thread cc via Digitalmars-d-learn

On Wednesday, 24 April 2024 at 05:08:25 UTC, Salih Dincer wrote:
Yes, `opApply()` works! You just need to use `do while()` 
instead of `while()` because it skips the first item.


It depends on the type of structure being consumed, if it 
provides "next" as a direct pointer then yeah you would need to 
consume the item first before iterating to the next in line.  
However some APIs provide an opaque iterator type where you call 
a "next" method to get the first element, IIRC Lua does something 
like this.


Re: Adapting foreign iterators to D ranges

2024-04-23 Thread Salih Dincer via Digitalmars-d-learn

On Tuesday, 23 April 2024 at 06:02:18 UTC, cc wrote:
Just to offer an alternative solution (since it sometimes gets 
overlooked), there is also the `opApply` approach.  You don't 
get full forward range status, and checking whether it's empty 
essentially requires doing something like std.algorithm 
`walkLength`, but if all you need is basic iteration, it can be 
a simpler solution:




Yes, `opApply()` works! You just need to use `do while()` instead 
of `while()` because it skips the first item.


```d
struct Node
{
  int item;
  Node* next;
}

class List
{
  Node* root, iter;

  this(int item = 0)
  {
iter = new Node(item, null);
root = iter;
  }

  List dup()
  {
auto backup = new List();
backup.root = root;
backup.iter = iter;

return backup;
  }

  void insertFront(T)(T item)
  {
(*iter).next = new Node(item, null);
this.Next;
  }

  bool empty() const => iter is null;
  auto front() inout => iter;
  auto popFront()=> iter = this.Next;
  auto getItem() => iter.item;
  auto rewind()  => iter = root;
}

auto Next(List list) => list.iter = list.iter.next;
auto gaussian(T)(T n)=> (n * n + n) / 2;

void main()
{
  import std.stdio;
  enum LIMIT = 10;

  auto list = new List(1);
  foreach(t; 2 .. LIMIT + 1)
  {
list.insertFront(t);
  }

  auto tmp = list.dup;
  list.rewind();

  size_t sum;
  do sum += list.getItem; while(list.Next);
  assert(gaussian(LIMIT) == sum);

  sum.writeln; // 55

  auto next = LIMIT + 1;
  tmp.insertFront(next);
  tmp.rewind();
  sum = 0;

  foreach(t; tmp) sum += t.item;
  assert(gaussian(LIMIT) + next == sum);

  sum.writeln; // 66
  tmp.rewind();

  auto range = Range(tmp);

  foreach(r; range) r.item.write(" ");
  writeln; //  2 3 4 5 6 7 8 9 10 11
  // ? (1) --^
}

struct Range
{
  private List iter;

  int opApply(scope int delegate(Node* t) dg)
  {
while(auto current = iter.Next)
{
  if (auto r = dg(current))
return r;
}
return 0;
  }
}
```

SDB@79




Re: Adapting foreign iterators to D ranges

2024-04-23 Thread cc via Digitalmars-d-learn

On Monday, 22 April 2024 at 11:36:43 UTC, Chloé wrote:
I wish to adapt this interface to a forward range for use with 
foreach and Phobos' range utilities. This amounts to 
implementing empty, front, and popFront, in terms of next and 
some state. But there is a choice to be made regarding the 
first call to next.


Just to offer an alternative solution (since it sometimes gets 
overlooked), there is also the `opApply` approach.  You don't get 
full forward range status, and checking whether it's empty 
essentially requires doing something like std.algorithm 
`walkLength`, but if all you need is basic iteration, it can be a 
simpler solution:


```d
struct Range {
private I iter;
this(I iter) { this.iter = iter; }
int opApply(scope int delegate(T* t) dg) {
while (auto current = next(iter)) {
if (auto r = dg(current))
return r;
}
return 0;
}
}
void main() {
I someIter; // = ...
auto range = Range(someIter);
foreach (const t; range) {
writeln(*t);
}
}
```


Re: Adapting foreign iterators to D ranges

2024-04-22 Thread Steven Schveighoffer via Digitalmars-d-learn

On Monday, 22 April 2024 at 11:36:43 UTC, Chloé wrote:

The first implementation has the advantage is being simpler and 
empty being const, but has the downside that next is called 
even if the range ends up not being used. Is either approach 
used consistently across the D ecosystem?


I always go for the simplest approach. So that means, pre-fill in 
the constructor.


Yes, the downside is, if you don't use it, then the iterator has 
moved, but the range hasn't. But returning to the iterator after 
using the range is always a dicey proposition anyway.


The huge benefit is that all the functions become simple and 
straightforward.


But there is no "right" approach. And using composition, you may 
be able to achieve all approaches with wrappers. Phobos does 
various things depending on what people thought was good at the 
time. It sometimes causes some very unexpected behavior.


I recommend always using the same approach for the same library, 
that way your users know what to expect!


-Steve


Re: Adapting foreign iterators to D ranges

2024-04-22 Thread Alexandru Ermicioi via Digitalmars-d-learn

On Monday, 22 April 2024 at 11:36:43 UTC, Chloé wrote:
The first implementation has the advantage is being simpler and 
empty being const, but has the downside that next is called 
even if the range ends up not being used. Is either approach 
used consistently across the D ecosystem?


You can also place initialization logic inside front, then empty 
could become const.


I don't think there is a preffered way of initializing such 
ranges, so imho consider what's best for your use case.




Adapting foreign iterators to D ranges

2024-04-22 Thread Chloé via Digitalmars-d-learn

Assume a third-party API of the following signature:

T* next(I iter);

which advances an iterator of sorts and returns the next element, or 
null when iteration is done. No other information about the state of the 
iterator is available.


I wish to adapt this interface to a forward range for use with foreach 
and Phobos' range utilities. This amounts to implementing empty, front, 
and popFront, in terms of next and some state. But there is a choice to 
be made regarding the first call to next.


One could call next during range construction:

struct Range
{
private I iter;
private T* current;
this(I iter) { this.iter = iter; current = next(iter); }
bool empty() const => current is null;
inout(T)* front() inout => current;
void popFront() { current = next(iter); }
}

Or do not call it until the first call to empty:

struct Range
{
private bool initialized;
private I iter;
private T* current;
this(I iter) { this.iter = iter; }
bool empty()
{
if (!initialized) {
current = next(iter);
initialized = true;
}
return current is null;
}
inout(T)* front() inout => current;
void popFront() { current = next(iter); }
}

The first implementation has the advantage is being simpler and empty 
being const, but has the downside that next is called even if the range 
ends up not being used. Is either approach used consistently across the 
D ecosystem?


Re: Key and value with ranges

2023-10-03 Thread christian.koestlin via Digitalmars-d-learn

On Tuesday, 3 October 2023 at 20:22:30 UTC, Andrey Zherikov wrote:
On Tuesday, 3 October 2023 at 19:57:06 UTC, christian.koestlin 
wrote:
On Tuesday, 3 October 2023 at 01:55:43 UTC, Andrey Zherikov 
wrote:
On Monday, 2 October 2023 at 18:46:14 UTC, christian.koestlin 
wrote:

[...]


Slightly improved:
```d
import std;

[...]


Thanks .. the thing with ref result is very clever!
Should `ref result` be `return result`?


`ref` might not be necessary fo AA but I'm not sure.
I found this in regards to AAs: 
https://forum.dlang.org/post/baewchcnyfibkvuiy...@forum.dlang.org
So it might be more efficient in this case to pass the AA as 
reference.



`fold` requires function to return something (see 
[doc](https://dlang.org/phobos/std_algorithm_iteration.html#fold)):
for each element x in range, result = fun(result, x) gets 
evaluated.
It's clear that the function needs to return something, but I was 
thinking if it would make sense to "document" how one works with 
the accumulator by indicating it as `return` instead of `ref`. I 
just tried to read through: 
https://dlang.org/spec/function.html#param-storage, but there is 
more to it .. like `return ref`, `return ref scope`, and what not 
.. so I am not so sure anymore.





Re: Key and value with ranges

2023-10-03 Thread Andrey Zherikov via Digitalmars-d-learn
On Tuesday, 3 October 2023 at 19:57:06 UTC, christian.koestlin 
wrote:
On Tuesday, 3 October 2023 at 01:55:43 UTC, Andrey Zherikov 
wrote:
On Monday, 2 October 2023 at 18:46:14 UTC, christian.koestlin 
wrote:

[...]


Slightly improved:
```d
import std;

[...]


Thanks .. the thing with ref result is very clever!
Should `ref result` be `return result`?


`ref` might not be necessary fo AA but I'm not sure.

`fold` requires function to return something (see 
[doc](https://dlang.org/phobos/std_algorithm_iteration.html#fold)):
for each element x in range, result = fun(result, x) gets 
evaluated.


Re: Key and value with ranges

2023-10-03 Thread christian.koestlin via Digitalmars-d-learn

On Tuesday, 3 October 2023 at 01:55:43 UTC, Andrey Zherikov wrote:
On Monday, 2 October 2023 at 18:46:14 UTC, christian.koestlin 
wrote:

[...]


Slightly improved:
```d
import std;

[...]


Thanks .. the thing with ref result is very clever!
Should `ref result` be `return result`?

Kind regards,
Christian


Re: Key and value with ranges

2023-10-02 Thread Joel via Digitalmars-d-learn

On Monday, 2 October 2023 at 02:47:37 UTC, Joel wrote:

```d
import std;

auto data=“I went for a walk, and fell down a hole.”;
```


[snip]



How can I improve this code? Like avoiding using foreach.


This works for me:

```d
import std;

auto data="I went for a walk, and fell down a hole.";

void main(string[] args)
{
if (args.length>1 && args[1].exists)
data=readText(args[1]);
data
.toLower
.map!(c => lowercase.canFind(std.uni.toLower(c)) ? c : ' 
')

.to!string
.split
.fold!((ref result, element) { result[element]+=1; return 
result; })(uint[string].init)

.byKeyValue
.array
.sort!"a.value>b.value"
.each!(pair => writeln("Word: ", pair.key, " - number of 
instances: ", pair.value));

}
```


Re: Key and value with ranges

2023-10-02 Thread Andrey Zherikov via Digitalmars-d-learn
On Monday, 2 October 2023 at 18:46:14 UTC, christian.koestlin 
wrote:

On Monday, 2 October 2023 at 02:47:37 UTC, Joel wrote:

How can I improve this code? Like avoiding using foreach.


You could fold into a hash that counts the occurrences like 
that:


```d
import std.uni : toLower;
import std.array : split, array;
import std.stdio : writeln;
import std.algorithm : fold, sort, map;


auto data="I went for a walk, and fell down a hole. a went";

int main(string[] args)
{
int[string] wc;
data
.toLower
.split
.fold!((result, element) { result[element] += 1; return 
result; })(wc)

.byKeyValue
.array
.sort!((pair1, pair2) => pair1.value > pair2.value)
.map!(pair => pair.key)
.writeln
;
return 0;
}
```

Not sure how to get rid of the declaration of the empty wc hash 
though.


Kind regards,
Christian


Slightly improved:
```d
import std;

auto data="I went for a walk, and fell down a hole. a went";

int main(string[] args)
{
data
.toLower
.split
.fold!((ref result, element) { ++result[element]; return 
result; })(uint[string].init)

.byKeyValue
.array
.sort!((pair1, pair2) => pair1.value > pair2.value)
.each!(pair => writeln("Word: ", pair.key, " - number of 
instances: ", pair.value))

;
return 0;
}
```

Output:
```
Word: a - number of instances: 3
Word: went - number of instances: 2
Word: and - number of instances: 1
Word: i - number of instances: 1
Word: hole. - number of instances: 1
Word: for - number of instances: 1
Word: down - number of instances: 1
Word: fell - number of instances: 1
Word: walk, - number of instances: 1
```


Re: Key and value with ranges

2023-10-02 Thread Salih Dincer via Digitalmars-d-learn

On Monday, 2 October 2023 at 20:20:44 UTC, Joel wrote:


I want the output sorted by value.


Look: 
https://forum.dlang.org/post/qjlmiohaoeolmoavw...@forum.dlang.org


```d
struct SIRALA(T) { }
```

You can use SIRALA(T).  Okay, his language is Turkish but our 
common language is D. His feature is that he uses double AA.  You 
will like it.  Here is the source:


https://gist.github.com/run-dlang/808633909615dbda431baa01f795484f

SDB@79





Re: Key and value with ranges

2023-10-02 Thread Joel via Digitalmars-d-learn

On Monday, 2 October 2023 at 02:47:37 UTC, Joel wrote:

```d
import std;

auto data=“I went for a walk, and fell down a hole.”;

void main(string[] args) {
int[string] dic;
struct WordCnt {
string word;
ulong count;
string toString() const {
return text("Word: ", word, " - number of 
instances: ", count);

}
}
WordCnt[] wc;
data
.map!(c => lowercase.canFind(std.uni.toLower(c)) ? c : 
' ')

.to!string
.splitter
.each!(d => dic[d]+=1);
foreach(key, value; dic)
wc~=WordCnt(key, value);
wc.sort!"a.count>b.count".each!writeln;
}
```

How can I improve this code? Like avoiding using foreach.


This is what I've got so far. Is there a way to do it any better?

```d
import std;

auto data="I went for a walk, and fell down a hole.";

void main() {
int[string] aa;
struct WordCnt {
string word;
ulong count;
string toString() const {
return text("Word: ", word, " - number of instances: 
", count);

}
}
WordCnt[] wc;
data
.map!(c => lowercase.canFind(std.uni.toLower(c)) ? c : ' 
')

.to!string
.splitter
.each!(d => aa[d]+=1);
aa.each!((key, value) {
wc~=WordCnt(key, value);
});
wc.sort!"a.count>b.count"
.each!writeln;
}
```


Re: Key and value with ranges

2023-10-02 Thread Joel via Digitalmars-d-learn

On Monday, 2 October 2023 at 06:19:29 UTC, Imperatorn wrote:

On Monday, 2 October 2023 at 02:47:37 UTC, Joel wrote:

```d
import std;

auto data=“I went for a walk, and fell down a hole.”;


You can improve it further by inlining

```d
import std;

auto data = "I went for a walk, and fell down a hole.";

void main(string[] args)
{
int[string] dic;

data.split(' ').each!(w => dic[w]++);
sort(dic.keys).each!(w => writeln(dic[w], " ",w));
}
```


I want the output sorted by value.


Re: Key and value with ranges

2023-10-02 Thread christian.koestlin via Digitalmars-d-learn

On Monday, 2 October 2023 at 02:47:37 UTC, Joel wrote:

How can I improve this code? Like avoiding using foreach.


You could fold into a hash that counts the occurrences like that:

```d
import std.uni : toLower;
import std.array : split, array;
import std.stdio : writeln;
import std.algorithm : fold, sort, map;


auto data="I went for a walk, and fell down a hole. a went";

int main(string[] args)
{
int[string] wc;
data
.toLower
.split
.fold!((result, element) { result[element] += 1; return 
result; })(wc)

.byKeyValue
.array
.sort!((pair1, pair2) => pair1.value > pair2.value)
.map!(pair => pair.key)
.writeln
;
return 0;
}
```

Not sure how to get rid of the declaration of the empty wc hash 
though.


Kind regards,
Christian


Re: Key and value with ranges

2023-10-02 Thread Imperatorn via Digitalmars-d-learn

On Monday, 2 October 2023 at 02:47:37 UTC, Joel wrote:

```d
import std;

auto data=“I went for a walk, and fell down a hole.”;


You can improve it further by inlining

```d
import std;

auto data = "I went for a walk, and fell down a hole.";

void main(string[] args)
{
int[string] dic;

data.split(' ').each!(w => dic[w]++);
sort(dic.keys).each!(w => writeln(dic[w], " ",w));
}
```


Re: Key and value with ranges

2023-10-01 Thread Imperatorn via Digitalmars-d-learn

On Monday, 2 October 2023 at 02:47:37 UTC, Joel wrote:

```d
import std;

auto data=“I went for a walk, and fell down a hole.”;

void main(string[] args) {
int[string] dic;
struct WordCnt {
string word;
ulong count;
string toString() const {
return text("Word: ", word, " - number of 
instances: ", count);

}
}
WordCnt[] wc;
data
.map!(c => lowercase.canFind(std.uni.toLower(c)) ? c : 
' ')

.to!string
.splitter
.each!(d => dic[d]+=1);
foreach(key, value; dic)
wc~=WordCnt(key, value);
wc.sort!"a.count>b.count".each!writeln;
}
```

How can I improve this code? Like avoiding using foreach.


You don't need a struct at all, you can just have an int[string] 
aa


Key and value with ranges

2023-10-01 Thread Joel via Digitalmars-d-learn

```d
import std;

auto data=“I went for a walk, and fell down a hole.”;

void main(string[] args) {
int[string] dic;
struct WordCnt {
string word;
ulong count;
string toString() const {
return text("Word: ", word, " - number of instances: 
", count);

}
}
WordCnt[] wc;
data
.map!(c => lowercase.canFind(std.uni.toLower(c)) ? c : ' 
')

.to!string
.splitter
.each!(d => dic[d]+=1);
foreach(key, value; dic)
wc~=WordCnt(key, value);
wc.sort!"a.count>b.count".each!writeln;
}
```

How can I improve this code? Like avoiding using foreach.


Re: Ranges

2022-08-07 Thread pascal111 via Digitalmars-d-learn

On Sunday, 7 August 2022 at 21:57:50 UTC, Ali Çehreli wrote:

On 8/7/22 08:34, pascal111 wrote:

> but after that in advanced level in programming, we should
> use pointers to do same tasks we were doing with slices (the
easy way of
> beginners).

That is an old thought. Today, we see that no matter how 
experienced, every person needs and appreciates help to prevent 
bugs. There are many cases of bugs killing people, jeopardizing 
expensive projects, loss of personal information, etc.


Ali


I think you are right that this is an old thought, I didn't 
noticed that, maybe it's because I didn't study C++ and know only 
about C, so I applied C features on D.


Re: Ranges

2022-08-07 Thread Ali Çehreli via Digitalmars-d-learn

On 8/6/22 22:58, Salih Dincer wrote:

> Ranges are not like that, all they do is
> generate.

You may be right. I've never seen it that way.

I've been under the following impression:

- C++'s iterators are based on an existing concept: pointers. Pointers 
are iterators.


- D's ranges are based on an existing concept: slices. Slices are ranges.

However, I can't find where I read that.

Ali



Re: Ranges

2022-08-07 Thread Ali Çehreli via Digitalmars-d-learn

On 8/7/22 08:34, pascal111 wrote:

> Everyone knows that slices are not pointers

D's slices are "fat pointers": In D's case, that translates to a pointer 
plus length.


> that pointers are real work,

Agreed. Pointers are fundamental features of CPUs.

> but slices are like a simple un-deep technique that is appropriate for
> beginners,

That is not correct. Slices are designed by a C expert to prevent 
horrible bugs caused by C experts. Most C experts use slices very happily.


> but after that in advanced level in programming, we should
> use pointers to do same tasks we were doing with slices (the easy way of
> beginners).

That is an old thought. Today, we see that no matter how experienced, 
every person needs and appreciates help to prevent bugs. There are many 
cases of bugs killing people, jeopardizing expensive projects, loss of 
personal information, etc.


Ali



Re: Ranges

2022-08-07 Thread pascal111 via Digitalmars-d-learn

On Sunday, 7 August 2022 at 19:53:06 UTC, ag0aep6g wrote:

On Sunday, 7 August 2022 at 15:34:19 UTC, pascal111 wrote:
Everyone knows that slices are not pointers that pointers are 
real work, but slices are like a simple un-deep technique that 
is appropriate for beginners, but after that in advanced level 
in programming, we should use pointers to do same tasks we 
were doing with slices (the easy way of beginners).


I can't tell if this is a joke or not.


It's just an opinion.


Re: Ranges

2022-08-07 Thread Emanuele Torre via Digitalmars-d-learn

On Saturday, 6 August 2022 at 15:37:32 UTC, pascal111 wrote:

On Friday, 5 August 2022 at 04:05:08 UTC, Salih Dincer wrote:

On Thursday, 4 August 2022 at 22:54:42 UTC, pascal111 wrote:


I didn't notice that all what we needs to pop a range forward 
is just a slice, yes, we don't need variable here.


Ranges and Slices are not the same thing. Slicing an array is 
easy. This is a language possibility. For example, you need an 
incrementing variable for the Fibonacci Series.


SDB@79


What!!! so where's ranges?! I thought slices of any array are 
ranges, and understood it like that, and also there's no data 
type called ranges, it's like if you are talking about Ghostly 
data type!


A range is like an iterator in any other language (Java, C++, 
python3, javascript, etc), it is how D implements (lazy) 
generators https://en.wikipedia.org/wiki/Lazy_evaluation .


Ranges/Iterators don't necessarily have to be backed by memory, 
they just have to implement the interface. In D, a `empty` bool 
function that tells you whether you are at the end of the range 
or not; a `front` function to get the current value if the range 
is not `empty`; and a void function named `popFront` to advance 
to the next value if the range is not `empty`.


Once you have implemented this interface, you can use your 
"range" object with any function that accept a range; with 
`foreach`; etc.


Example of a range that is not backed by memory is a range with 
all the integer numbers.


```D
struct Integers {
  private int z = 0;

  /* or make it a bool attribute that starts as false, and you 
set to

   * true when popFront is called while z is equal to int.min */
  public bool empty() { return false; }

  public int front() { return this.z; }

  public void popFront()
  {
/* if (this.z == int.min) { this.empty = false; return; } */
this.z *= -1;
if (this.z <= 0)
  --this.z;
  }
}

void main()
{
  import std.stdio : writeln;

  /* foreach is syntax sugar for
   *   for (auto r = Integers(); !r.empty(); r.popFront()) {
   * auto z = r.front(); /+ or  const z = r.front();  or ... 
+/

   * ...
   *   }
   * that is why it only works with ranges.
   */
  foreach (const z; Integers()) {
writeln(z);
if (z == 5)
  break;
  }
}
```

output:
```
0
-1
1
-2
2
-3
3
-4
4
-5
5
```

This will iterate all the integers, and the integers are of 
course, not
all in memory, and don't remain in memory after they are used, 
since
that would require infinite memory. (in the case of a range of 
integers,
not infinite, because they are constrained by being int.sizeof 
bytes,
but you could use a bignum implemenation that is not constrained 
by

that and they would actually be infinite.)

---

The equivalent in Java is the Iterable/Iterator interface.

```java
import java.util.Iterator;

public class Integers
  implements Iterable
{
  public class IntegersIterator
implements Iterator
  {
private int z = 0;
private boolean first = true;

public IntegersIterator(Integer z)
{
  this.z = z;
}

@Override
public boolean hasNext() { return true; }

@Override
public Integer next()
{
  if (this.first) {
this.first = false;
return this.z;
  }

  this.z *= -1;
  if (this.z <= 0)
--this.z;
  return this.z;
}
  }

  @Override
  public IntegersIterator iterator() { return new 
IntegersIterator(0); }


  public static void main(String[] args)
  {
/* syntax sugar for
 *   {
 * final var it = newIntegers.iterator();
 * while (it.hasNext()) {
 *   final int z = it.next();
 *   ...
 * }
 *   }
 */
for (final int z : new Integers()) {
  System.out.println(z);
  if (z == 5)
break;
}
  }
}
```

The equivalent in python is a generator function:
```python
def integers():
  z = 0
  yield z
  while True:
z *= -1
if z <= 0:
  z -= 1
yield z

for z in integers():
  print(z)
  if z == 5:
break
```

etc



Re: Ranges

2022-08-07 Thread ag0aep6g via Digitalmars-d-learn

On Sunday, 7 August 2022 at 15:34:19 UTC, pascal111 wrote:
Everyone knows that slices are not pointers that pointers are 
real work, but slices are like a simple un-deep technique that 
is appropriate for beginners, but after that in advanced level 
in programming, we should use pointers to do same tasks we were 
doing with slices (the easy way of beginners).


I can't tell if this is a joke or not.


Re: Ranges

2022-08-07 Thread Salih Dincer via Digitalmars-d-learn

On Sunday, 7 August 2022 at 15:34:19 UTC, pascal111 wrote:
Everyone knows that slices are not pointers that pointers are 
real work, but slices are like a simple un-deep technique that 
is appropriate for beginners, but after that in advanced level 
in programming, we should use pointers to do same tasks we were 
doing with slices (the easy way of beginners).


The following information about slices may be helpful:

Slices are objects from type T[] for any given type T. Slices 
provide a view on a subset of an array of T values - or just 
point to the whole array. Slices and dynamic arrays are the 
same.


A slice consists of two members - a pointer to the starting 
element and the length of the slice:

```d
T* ptr;
size_t length; // unsigned 32 bit on 32bit, unsigned 64 bit on 
64bit

```
[...]

**Source:** https://tour.dlang.org/tour/en/basics/slices

SDB@79


Re: Ranges

2022-08-07 Thread pascal111 via Digitalmars-d-learn

On Sunday, 7 August 2022 at 05:12:38 UTC, Ali Çehreli wrote:

On 8/6/22 14:10, pascal111 wrote:




> a powerful point in the account of C.

I missed how you made that connection.

Everyone knows that slices are not pointers that pointers are 
real work, but slices are like a simple un-deep technique that is 
appropriate for beginners, but after that in advanced level in 
programming, we should use pointers to do same tasks we were 
doing with slices (the easy way of beginners).





Re: Ranges

2022-08-07 Thread Salih Dincer via Digitalmars-d-learn

On Saturday, 6 August 2022 at 17:29:30 UTC, Ali Çehreli wrote:

On 8/6/22 09:33, Salih Dincer wrote:

> the slices feel like ranges, don't they?

Yes because they are ranges. :) (Maybe you meant they don't 
have range member functions, which is true.)


Slices use pointers.  Do I need to tell you what the pointers do! 
 Each of them points to a data.  Ranges are not like that, all 
they do is generate.  Ok, you use a slice just as if it were a 
range.  But they are not ranges.


SDB@79


Re: Ranges

2022-08-06 Thread Ali Çehreli via Digitalmars-d-learn

On 8/6/22 14:10, pascal111 wrote:

> the problem is that ranges in D lack the usage of pointers as
> an essential tool to make all of ranges functions they need. If ranges
> exist in C, they would use pointers, and this is

There are a few cases where pointers provide functionality that ranges 
cannot:


1) Some algorithms don't make much sense with ranges. For example, most 
of the time find() can return just the element that we seek. In D, 
find() returns a range so that we can chain it with other algorithms.


2) Some algorithms like partition() better use three pointers.

Other than that, ranges are superior to pointers in every aspect. (I 
resent the fact that some C++ "experts" used those two points to decide 
ranges are inferior and helped deprive the C++ community of ranges for a 
very long time. The same "experts" did the same with 'static if'.)


> a powerful point in the account of C.

I missed how you made that connection.

Ali



Re: Ranges

2022-08-06 Thread pascal111 via Digitalmars-d-learn

On Saturday, 6 August 2022 at 15:54:57 UTC, H. S. Teoh wrote:
On Sat, Aug 06, 2022 at 03:37:32PM +, pascal111 via 
Digitalmars-d-learn wrote:

On Friday, 5 August 2022 at 04:05:08 UTC, Salih Dincer wrote:
> On Thursday, 4 August 2022 at 22:54:42 UTC, pascal111 wrote:
> > [...]
> 
> Ranges and Slices are not the same thing. Slicing an array 
> is easy. This is a language possibility. For example, you 
> need an incrementing variable for the Fibonacci Series.
> 
> SDB@79


What!!! so where's ranges?! I thought slices of any array are 
ranges, and understood it like that, and also there's no data 
type called ranges, it's like if you are talking about Ghostly 
data type!


A range is any type that supports the Range API defined in 
std.range (i.e., .empty, .front, .popFront). For more 
explanations, read:


http://www.informit.com/articles/printerfriendly.aspx?p=1407357=1
http://ddili.org/ders/d.en/ranges.html
http://dconf.org/2015/talks/davis.html
http://tour.dlang.org/tour/en/basics/ranges
http://wiki.dlang.org/Component_programming_with_ranges


T


You know, the problem is that ranges in D lack the usage of 
pointers as an essential tool to make all of ranges functions 
they need. If ranges exist in C, they would use pointers, and 
this is a powerful point in the account of C.


Re: Ranges

2022-08-06 Thread Ali Çehreli via Digitalmars-d-learn

On 8/6/22 09:33, Salih Dincer wrote:

> the slices feel like ranges, don't they?

Yes because they are ranges. :) (Maybe you meant they don't have range 
member functions, which is true.)


D's slices happen to be the most capable range kind: RandonAccessRange. 
All of the following operations are supported on them as long as one 
imports std.array (or std.range, which publicly does so):


- empty
- front
- popFront
- save
- back
- popBack
- indexed element access

Slices have the optional length property as well (i.e. hasLength).

Those operations are not supported by member functions but by 
free-standing functions.


Ali



Re: Ranges

2022-08-06 Thread Salih Dincer via Digitalmars-d-learn

On Saturday, 6 August 2022 at 16:30:55 UTC, Salih Dincer wrote:
Indeed, the slices (we can call it a dynamic array) feel like 
slice, don't they?


Edit: Indeed, the slices feel like ranges, don't they?

Sorry...

SDB@79




Re: Ranges

2022-08-06 Thread Salih Dincer via Digitalmars-d-learn

On Saturday, 6 August 2022 at 15:37:32 UTC, pascal111 wrote:

On Friday, 5 August 2022 at 04:05:08 UTC, Salih Dincer wrote:

On Thursday, 4 August 2022 at 22:54:42 UTC, pascal111 wrote:


I didn't notice that all what we needs to pop a range forward 
is just a slice, yes, we don't need variable here.


Ranges and Slices are not the same thing. Slicing an array is 
easy. This is a language possibility. For example, you need an 
incrementing variable for the Fibonacci Series.


SDB@79


What!!! so where's ranges?! I thought slices of any array are 
ranges, and understood it like that, and also there's no data 
type called ranges, it's like if you are talking about Ghostly 
data type!


Well, that's very normal. Because as you work with ranges, you 
will understand better. Indeed, the slices (we can call it a 
dynamic array) feel like slice, don't they?


SDB@79


Re: Ranges

2022-08-06 Thread H. S. Teoh via Digitalmars-d-learn
On Sat, Aug 06, 2022 at 03:37:32PM +, pascal111 via Digitalmars-d-learn 
wrote:
> On Friday, 5 August 2022 at 04:05:08 UTC, Salih Dincer wrote:
> > On Thursday, 4 August 2022 at 22:54:42 UTC, pascal111 wrote:
> > > 
> > > I didn't notice that all what we needs to pop a range forward is
> > > just a slice, yes, we don't need variable here.
> > 
> > Ranges and Slices are not the same thing. Slicing an array is easy.
> > This is a language possibility. For example, you need an
> > incrementing variable for the Fibonacci Series.
> > 
> > SDB@79
> 
> What!!! so where's ranges?! I thought slices of any array are ranges,
> and understood it like that, and also there's no data type called
> ranges, it's like if you are talking about Ghostly data type!

A range is any type that supports the Range API defined in std.range
(i.e., .empty, .front, .popFront). For more explanations, read:

http://www.informit.com/articles/printerfriendly.aspx?p=1407357=1
http://ddili.org/ders/d.en/ranges.html
http://dconf.org/2015/talks/davis.html
http://tour.dlang.org/tour/en/basics/ranges
http://wiki.dlang.org/Component_programming_with_ranges


T

-- 
No! I'm not in denial!


Re: Ranges

2022-08-06 Thread pascal111 via Digitalmars-d-learn

On Friday, 5 August 2022 at 04:05:08 UTC, Salih Dincer wrote:

On Thursday, 4 August 2022 at 22:54:42 UTC, pascal111 wrote:


I didn't notice that all what we needs to pop a range forward 
is just a slice, yes, we don't need variable here.


Ranges and Slices are not the same thing. Slicing an array is 
easy. This is a language possibility. For example, you need an 
incrementing variable for the Fibonacci Series.


SDB@79


What!!! so where's ranges?! I thought slices of any array are 
ranges, and understood it like that, and also there's no data 
type called ranges, it's like if you are talking about Ghostly 
data type!


Re: Ranges

2022-08-05 Thread H. S. Teoh via Digitalmars-d-learn
On Fri, Aug 05, 2022 at 08:06:00AM -0700, Ali Çehreli via Digitalmars-d-learn 
wrote:
> [...] I realized that the following fails with a RangeError:
> 
> void main() {
>   auto arr = [1, 2, 3];
>   arr[0..$-1] = arr[1..$];// <-- Runtime error
> }
> 
> I suspect the length of the array is stamped too soon. (?)
> 
> Should that operation be supported?
[...]

This is why in C there's a difference between memcpy and memmove.  I
don't know how to express the equivalent in D, though. In general, you
can't tell until runtime whether two slices overlap (`arr` could be
aliased by another slice, for example, so you can't just tell by whether
you're copying an overlapping range from the same variable).

But if you know beforehand the ranges being copied are overlapping, you
could use std.algorithm.bringToFront which would do the Right Thing(tm)
in this case.


T

-- 
Why are you blatanly misspelling "blatant"? -- Branden Robinson


Re: Ranges

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

On 8/5/22 01:59, frame wrote:
> On Thursday, 4 August 2022 at 22:14:26 UTC, Ali Çehreli wrote:
>
>> No element is copied or moved. :)
>>
>> Ali
>
> I know that :)

And I know that. :) We don't know who else is reading these threads, so 
I didn't want to give wrong impression.


Copying would happen if we added slicing on the left-hand side. However, 
I realized that the following fails with a RangeError:


void main() {
  auto arr = [1, 2, 3];
  arr[0..$-1] = arr[1..$];// <-- Runtime error
}

I suspect the length of the array is stamped too soon. (?)

Should that operation be supported?

Ali



Re: Ranges

2022-08-05 Thread frame via Digitalmars-d-learn

On Thursday, 4 August 2022 at 22:14:26 UTC, Ali Çehreli wrote:


No element is copied or moved. :)

Ali


I know that :) I just found that this user has problems to 
understand basics in D, so I tried not to go in detail and keep 
at its kind of logical layer. It seems the better way to help 
until the user asks specific questions.


Re: Ranges

2022-08-04 Thread Salih Dincer via Digitalmars-d-learn

On Thursday, 4 August 2022 at 22:54:42 UTC, pascal111 wrote:


I didn't notice that all what we needs to pop a range forward 
is just a slice, yes, we don't need variable here.


Ranges and Slices are not the same thing. Slicing an array is 
easy. This is a language possibility. For example, you need an 
incrementing variable for the Fibonacci Series.


SDB@79



Re: Ranges

2022-08-04 Thread pascal111 via Digitalmars-d-learn

On Thursday, 4 August 2022 at 22:14:26 UTC, Ali Çehreli wrote:

On 8/4/22 11:05, frame wrote:

> `popFront()`

The function was this:

   void popFront() {
  students = students[1 .. $];
   }

> copies all
> elements except the first one into the variable (and
overwrites it), so
> it moves the data forward.

That would be very slow. :) What actually happens is, just the 
two variables that define a slice is adjusted.


Slices consist of two members:

struct __an_int_D_array_behind_the_scenes {
  size_t length;
  int * ptr;
}

So,

  students = students[1..$];

is the same as doing the following:

  students.length = (students.length - 1);
  students.ptr = students.ptr + 1;

(ptr's value would change by 4 bytes because 'int'.)

No element is copied or moved. :)

Ali


I didn't notice that all what we needs to pop a range forward is 
just a slice, yes, we don't need variable here.


Re: Ranges

2022-08-04 Thread Ali Çehreli via Digitalmars-d-learn

On 8/4/22 11:05, frame wrote:

> `popFront()`

The function was this:

   void popFront() {
  students = students[1 .. $];
   }

> copies all
> elements except the first one into the variable (and overwrites it), so
> it moves the data forward.

That would be very slow. :) What actually happens is, just the two 
variables that define a slice is adjusted.


Slices consist of two members:

struct __an_int_D_array_behind_the_scenes {
  size_t length;
  int * ptr;
}

So,

  students = students[1..$];

is the same as doing the following:

  students.length = (students.length - 1);
  students.ptr = students.ptr + 1;

(ptr's value would change by 4 bytes because 'int'.)

No element is copied or moved. :)

Ali



Re: Ranges

2022-08-04 Thread Ali Çehreli via Digitalmars-d-learn

On 8/4/22 06:08, pascal111 wrote:
> In next code from
> "https://www.tutorialspoint.com/d_programming/d_programming_ranges.htm;,

That page seems to be adapted from this original:

  http://ddili.org/ders/d.en/ranges.html

> we have two issues:
>
> 1) Why the programmer needs to program "empty()", "front()", and
> "popFront()" functions for ranges

The programmer almost never needs to implement those functions. Existing 
data structures and algorithms are almost always sufficient. (I did need 
to implement them but really rarely.)


I tried to explain what those functions do. I don't like my Students 
example much because wrapping a D slice does not make much sense. Again, 
I just try to explain them.


> while they exist in the language
> library?

The existing front, popFronh, etc. are only for arrays (slices).

> it seems there's no need to exert efforts for that.

Exactly.

> "https://dlang.org/phobos/std_range_primitives.html;
>
> 2) "front()", and "popFront()" are using fixed constants to move forward
> the range, while they should use variables.

Well, 0 is always the first element and 1..$ are always the rest. 
Variables would not add any value there.


However, the example could use the existing library function that you 
mention:


> @property bool empty() const {
>return students.length == 0;

Better:

 import std.array : empty;
 return students.empty;

> }
> @property ref Student front() {
>return students[0];

Better:

 import std.array : front;
 return students.front;

> }
> void popFront() {
>students = students[1 .. $];

Better:

 import std.array : popFront;
 students.popFront();

But I think those implementations might confuse the reader.

Ali



Re: Ranges

2022-08-04 Thread frame via Digitalmars-d-learn

On Thursday, 4 August 2022 at 13:08:21 UTC, pascal111 wrote:

1) Why the programmer needs to program "empty()", "front()", 
and "popFront()" functions for ranges while they exist in the 
language library? it seems there's no need to exert efforts for 
that. "https://dlang.org/phobos/std_range_primitives.html;


- These functions are wrappers to use something as range
- Ranges need to implement the functions to keep their data 
private, also there are complex types the need to handle data 
differently
- Ranges must implement the functions so other function can 
recognize it as such (eg. `isInputRange`) - there is no common 
interface, it's determined by compile time


2) "front()", and "popFront()" are using fixed constants to 
move forward the range, while they should use variables.


`front()` is always using the first element BUT `popFront()` 
copies all elements except the first one into the variable (and 
overwrites it), so it moves the data forward.


Re: Ranges

2022-08-04 Thread Salih Dincer via Digitalmars-d-learn

On Thursday, 4 August 2022 at 13:08:21 UTC, pascal111 wrote:

```D
import std.stdio;
import std.string;

struct Student {
   string name;
   int number;

   string toString() const {
  return format("%s(%s)", name, number);
   }
}

struct School {
   Student[] students;
}
struct StudentRange {
   Student[] students;

   this(School school) {
  this.students = school.students;
   }
   @property bool empty() const {
  return students.length == 0;
   }
   @property ref Student front() {
  return students[0];
   }
   void popFront() {
  students = students[1 .. $];
   }
}

void main() {
   auto school = School([ Student("Raj", 1), 
Student("John", 2), Student("Ram", 3)]);

   auto range = StudentRange(school);
   writeln(range);

   writeln(school.students.length);

   writeln(range.front);

   range.popFront;

   writeln(range.empty);
   writeln(range);
}
```





Ranges

2022-08-04 Thread pascal111 via Digitalmars-d-learn
In next code from 
"https://www.tutorialspoint.com/d_programming/d_programming_ranges.htm;, we have two issues:


1) Why the programmer needs to program "empty()", "front()", and 
"popFront()" functions for ranges while they exist in the 
language library? it seems there's no need to exert efforts for 
that. "https://dlang.org/phobos/std_range_primitives.html;


2) "front()", and "popFront()" are using fixed constants to move 
forward the range, while they should use variables.



'''D
import std.stdio;
import std.string;

struct Student {
   string name;
   int number;

   string toString() const {
  return format("%s(%s)", name, number);
   }
}

struct School {
   Student[] students;
}
struct StudentRange {
   Student[] students;

   this(School school) {
  this.students = school.students;
   }
   @property bool empty() const {
  return students.length == 0;
   }
   @property ref Student front() {
  return students[0];
   }
   void popFront() {
  students = students[1 .. $];
   }
}

void main() {
   auto school = School([ Student("Raj", 1), Student("John", 
2), Student("Ram", 3)]);

   auto range = StudentRange(school);
   writeln(range);

   writeln(school.students.length);

   writeln(range.front);

   range.popFront;

   writeln(range.empty);
   writeln(range);
}
'''


Re: Why does inputRangeObject fail to derive correctly for RandomAccessInfinite ranges?

2022-07-16 Thread D Lark via Digitalmars-d-learn

On Saturday, 16 July 2022 at 12:40:09 UTC, Paul Backus wrote:

On Saturday, 16 July 2022 at 08:40:10 UTC, D Lark wrote:

On Wednesday, 13 July 2022 at 01:40:43 UTC, Paul Backus wrote:

On Wednesday, 13 July 2022 at 01:23:35 UTC, D Lark wrote:

[...]


Yes, it should behave the way you expect. The current 
behavior is a bug.


I've submitted a report for it here: 
https://issues.dlang.org/show_bug.cgi?id=23242


It looks like the resolution is that this can't be fixed? I'm 
not sure I understand the conclusion. What does this mean for 
this part of the library then? Should the 
RandomAccessInfinite!E interface be removed since it's not 
fully supported?


The interface works fine, it's just that the 
`InputRangeObject!R` instance itself does not satisfy 
`isRandomAccessRange`:


```d
auto seqObj  = sequence!((a, n) => n).inputRangeObject;
RandomAccessInfinite!size_t seqIface = seqObj;

static assert( isRandomAccessRange!(typeof(seqIface))); // 
interface passes
static assert(!isRandomAccessRange!(typeof(seqObj))); // object 
fails

```

So if you code to the interfaces and ignore the concrete type 
of the range object, you should not have any problems.


Thanks this definitely alleviates the issue somewhat.

However it is definitely surprising that an object which 
literally derives from an interface cannot be tested to implement 
said interface. Is this inconsistency not a problem?


Re: Why does inputRangeObject fail to derive correctly for RandomAccessInfinite ranges?

2022-07-16 Thread Paul Backus via Digitalmars-d-learn

On Saturday, 16 July 2022 at 08:40:10 UTC, D Lark wrote:

On Wednesday, 13 July 2022 at 01:40:43 UTC, Paul Backus wrote:

On Wednesday, 13 July 2022 at 01:23:35 UTC, D Lark wrote:
First, please can someone clarify if the behaviour I expect 
in the last line is consistent with the intention of the 
library?


Yes, it should behave the way you expect. The current behavior 
is a bug.


I've submitted a report for it here: 
https://issues.dlang.org/show_bug.cgi?id=23242


It looks like the resolution is that this can't be fixed? I'm 
not sure I understand the conclusion. What does this mean for 
this part of the library then? Should the 
RandomAccessInfinite!E interface be removed since it's not 
fully supported?


The interface works fine, it's just that the `InputRangeObject!R` 
instance itself does not satisfy `isRandomAccessRange`:


```d
auto seqObj  = sequence!((a, n) => n).inputRangeObject;
RandomAccessInfinite!size_t seqIface = seqObj;

static assert( isRandomAccessRange!(typeof(seqIface))); // 
interface passes
static assert(!isRandomAccessRange!(typeof(seqObj))); // object 
fails

```

So if you code to the interfaces and ignore the concrete type of 
the range object, you should not have any problems.


Re: Why does inputRangeObject fail to derive correctly for RandomAccessInfinite ranges?

2022-07-16 Thread D Lark via Digitalmars-d-learn

On Saturday, 16 July 2022 at 08:40:10 UTC, D Lark wrote:

On Wednesday, 13 July 2022 at 01:40:43 UTC, Paul Backus wrote:

On Wednesday, 13 July 2022 at 01:23:35 UTC, D Lark wrote:
First, please can someone clarify if the behaviour I expect 
in the last line is consistent with the intention of the 
library?


Yes, it should behave the way you expect. The current behavior 
is a bug.


I've submitted a report for it here: 
https://issues.dlang.org/show_bug.cgi?id=23242


It looks like the resolution is that this can't be fixed? I'm 
not sure I understand the conclusion. What does this mean for 
this part of the library then? Should the 
RandomAccessInfinite!E interface be removed since it's not 
fully supported?


What if infinite-ness is a class/struct level property distinct 
from empty-ness like  `const enum bool infinite`? It appears to 
me that this is what we try to achieve by the unfortunate 
coupling of declaring `empty` as a manifest constant to denote an 
infinite range. If we have a distinct convention for declaring 
infiniteness then for the infinite range case we can insert `bool 
empty(){return false;}` (or `const enum bool = false)` 
automatically or check agreement. We can even have a mixin like 
the `ImplementLength` one that can add both `infinite` and 
`empty` in one go, which might be useful for implementing 
infinite ranges.


Re: Why does inputRangeObject fail to derive correctly for RandomAccessInfinite ranges?

2022-07-16 Thread D Lark via Digitalmars-d-learn

On Wednesday, 13 July 2022 at 01:40:43 UTC, Paul Backus wrote:

On Wednesday, 13 July 2022 at 01:23:35 UTC, D Lark wrote:
First, please can someone clarify if the behaviour I expect in 
the last line is consistent with the intention of the library?


Yes, it should behave the way you expect. The current behavior 
is a bug.


I've submitted a report for it here: 
https://issues.dlang.org/show_bug.cgi?id=23242


It looks like the resolution is that this can't be fixed? I'm not 
sure I understand the conclusion. What does this mean for this 
part of the library then? Should the RandomAccessInfinite!E 
interface be removed since it's not fully supported?


Re: Why does inputRangeObject fail to derive correctly for RandomAccessInfinite ranges?

2022-07-12 Thread Paul Backus via Digitalmars-d-learn

On Wednesday, 13 July 2022 at 01:23:35 UTC, D Lark wrote:
First, please can someone clarify if the behaviour I expect in 
the last line is consistent with the intention of the library?


Yes, it should behave the way you expect. The current behavior is 
a bug.


I've submitted a report for it here: 
https://issues.dlang.org/show_bug.cgi?id=23242


Why does inputRangeObject fail to derive correctly for RandomAccessInfinite ranges?

2022-07-12 Thread D Lark via Digitalmars-d-learn
This is slightly related to this issue I reported earlier: 
https://forum.dlang.org/post/wghdwxptjfcjwptny...@forum.dlang.org


Here's the snippet that captures what fails for me

```dlang
import std.range: ElementType, isRandomAccessRange, 
inputRangeObject, sequence, MostDerivedInputRange, 
RandomAccessInfinite;


auto seq = sequence!((a, n) => n);
alias SeqType = typeof(seq);
static assert(isRandomAccessRange!SeqType);

static assert(is(MostDerivedInputRange!SeqType == 
RandomAccessInfinite!(ElementType!SeqType)));


auto seqInputRange = seq.inputRangeObject;
static assert(isRandomAccessRange!(typeof(seqInputRange))); 
// returns 'false'; I expect this to return 'true' as the most 
derived type of 'seq' is of 'RandomAccessInfinite' as 
demonstrated above


```
I am compiling using dmd v2.100.1

First, please can someone clarify if the behaviour I expect in 
the last line is consistent with the intention of the library?


I have poked around the source and I can see that the 
`InputRangeObject` (the object returned by `inputRangeObject` 
derives from the return type of the `MostDerivedInputRange` 
template, which as shown above returns the correct interface. 
However it seems that the implementation of `InputRangeObject` 
does not implement `enum bool empty = false` in the case of the 
`RandomAccessInfinite` ranges, which leads to the template 
`isRandomAccessRange` returning false.


Re: Dynamic chain for ranges?

2022-06-13 Thread Ola Fosheim Grøstad via Digitalmars-d-learn
On Monday, 13 June 2022 at 14:03:13 UTC, Steven Schveighoffer 
wrote:
Merge sort only works if it's easy to manipulate the structure, 
like a linked-list, or to build a new structure, like if you 
don't care about allocating a new array every iteration.


The easiest option is to have two buffers that can hold all 
items, in the last merge you merge back to the input-storage. But 
yeah, it is only «fast» for very large arrays.




Re: Dynamic chain for ranges?

2022-06-13 Thread Steven Schveighoffer via Digitalmars-d-learn

On 6/13/22 9:44 AM, Ola Fosheim Grøstad wrote:

On Monday, 13 June 2022 at 13:22:52 UTC, Steven Schveighoffer wrote:
I would think sort(joiner([arr1, arr2, arr3])) should work, but it's 
not a random access range.


Yes, I got the error «must satisfy the following constraint: 
isRandomAccessRange!Range`».


It would be relatively easy to make it work as a random access range if 
arr1, arr2, etc were fixed size slices.


Or I guess, use insertion-sort followed by merge-sort.


Merge sort only works if it's easy to manipulate the structure, like a 
linked-list, or to build a new structure, like if you don't care about 
allocating a new array every iteration.


-Steve


Re: Dynamic chain for ranges?

2022-06-13 Thread Ola Fosheim Grøstad via Digitalmars-d-learn
On Monday, 13 June 2022 at 13:22:52 UTC, Steven Schveighoffer 
wrote:
I would think sort(joiner([arr1, arr2, arr3])) should work, but 
it's not a random access range.


Yes, I got the error «must satisfy the following constraint: 
isRandomAccessRange!Range`».


It would be relatively easy to make it work as a random access 
range if arr1, arr2, etc were fixed size slices.


Or I guess, use insertion-sort followed by merge-sort.





Re: Dynamic chain for ranges?

2022-06-13 Thread Steven Schveighoffer via Digitalmars-d-learn

On 6/13/22 4:51 AM, Ola Fosheim Grøstad wrote:
Is there a dynamic chain primitive, so that you can add to the chain at 
runtime?


Context: the following example on the front page is interesting.

```d
void main()
{
     int[] arr1 = [4, 9, 7];
     int[] arr2 = [5, 2, 1, 10];
     int[] arr3 = [6, 8, 3];
     sort(chain(arr1, arr2, arr3));
     writefln("%s\n%s\n%s\n", arr1, arr2, arr3);
}
```

But it would be much more useful in practice if "chain" was a dynamic 
array.




`chain` allows ranges of different types.
`joiner` should be the equivalent for a dynamic range of ranges of the 
same type.


I would think sort(joiner([arr1, arr2, arr3])) should work, but it's not 
a random access range. Most likely because the big-O constants are no 
longer constant.


-Steve


Re: Dynamic chain for ranges?

2022-06-13 Thread Ola Fosheim Grøstad via Digitalmars-d-learn

On Monday, 13 June 2022 at 09:08:40 UTC, Salih Dincer wrote:
On Monday, 13 June 2022 at 08:51:03 UTC, Ola Fosheim Grøstad 
wrote:


But it would be much more useful in practice if "chain" was a 
dynamic array.


Already so:


I meant something like: chain = [arr1, arr2, …, arrN]

I don't use ranges, but I thought this specific use case could be 
valuable.


Imagine you have a chunked datastructure of unknown lengths, and 
you want to "redistribute" items without reallocation.




Re: Dynamic chain for ranges?

2022-06-13 Thread Salih Dincer via Digitalmars-d-learn
On Monday, 13 June 2022 at 08:51:03 UTC, Ola Fosheim Grøstad 
wrote:


But it would be much more useful in practice if "chain" was a 
dynamic array.


Already so:

```d
int[] arr = [4, 9, 7, 5, 2, 1, 10, 6, 8, 3];

int[] arr1 = arr[0..3];
int[] arr2 = arr[3..7];
int[] arr3 = arr[7..$];

sort(chain(arr1, arr2, arr3));
writefln("%s\n%s\n%s\n", arr1, arr2, arr3);
typeid(arr).writeln(": ", arr);
writeln([0], " == ", [0]);

/* Print Out:
[1, 2, 3]
[4, 5, 6, 7]
[8, 9, 10]

int[]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
7F7FBE348000 == 7F7FBE348000
*/
```


Dynamic chain for ranges?

2022-06-13 Thread Ola Fosheim Grøstad via Digitalmars-d-learn
Is there a dynamic chain primitive, so that you can add to the 
chain at runtime?


Context: the following example on the front page is interesting.

```d
void main()
{
int[] arr1 = [4, 9, 7];
int[] arr2 = [5, 2, 1, 10];
int[] arr3 = [6, 8, 3];
sort(chain(arr1, arr2, arr3));
writefln("%s\n%s\n%s\n", arr1, arr2, arr3);
}
```

But it would be much more useful in practice if "chain" was a 
dynamic array.




Re: number ranges

2022-01-21 Thread H. S. Teoh via Digitalmars-d-learn
> On Friday, 21 January 2022 at 17:25:20 UTC, Ali Çehreli wrote:
[...]
> > Additionally, just because we *provide* a step, now we *require*
> > division from all types (making it very cumbersome for user-defined
> > types).
[...]

It doesn't have to be this way. We could just use DbI to inspect whether
the incoming type supports division; if it does, we provide stepping,
otherwise, just plain ole iteration.

DbI rocks.


T

-- 
Doubt is a self-fulfilling prophecy.


Re: number ranges

2022-01-21 Thread Salih Dincer via Digitalmars-d-learn

On Friday, 21 January 2022 at 17:25:20 UTC, Ali Çehreli wrote:


Ouch! I tried the following code, my laptop got very hot, it's 
been centuries, and it's still running! :p


:)


```d
   size_t length() inout {
 auto len = 1 + (last - first) / step;
 return cast(size_t)len;
   }
```
Does that not return 1 for an empty range?


Yes, but it will never return an empty range:

```d
  enum e = 1;
  auto o = inclusiveRange(e, e); // only one element
  assert(!o.empty);
  assert(o.length == e);
  assert(o.equal([e]));
```

Additionally, just because we *provide* a step, now we 
*require* division from all types (making it very cumbersome 
for user-defined types).


I don't quite understand what you mean?

Salih



Re: number ranges

2022-01-21 Thread Ali Çehreli via Digitalmars-d-learn

On 1/21/22 08:58, Salih Dincer wrote:

> ```d
> auto inclusiveRange(T)(T f, T l, T s = cast(T)0)
> in(!isBoolean!T) {

'in' contracts are checked at runtime. The one above does not make sense 
because you already disallow compilation for 'bool' below.


You could add a template constraint there:

auto inclusiveRange(T)(T f, T l, T s = cast(T)0)
if(!isBoolean!T) {

(Note 'if' vs. 'in'.)

However, people who instantiate the struct template directly would 
bypass that check anyway.


>static assert(!isBoolean!T, "\n
>Cannot be used with bool type\n");
>if(!s) s++;
>return InclusiveRange!T(f, l, s);
> }

>bool opBinaryRight(string op:"in")(T rhs) {
>  foreach(r; this) {
>if(r == rhs) return true;
>  }
>  return false;
>}

Ouch! I tried the following code, my laptop got very hot, it's been 
centuries, and it's still running! :p


  auto looong = inclusiveRange(ulong.min, ulong.max);
  foreach (l; looong) {
assert(l in looong);
  }

>size_t length() inout {
>  auto len = 1 + (last - first) / step;
>  return cast(size_t)len;
>}

Does that not return 1 for an empty range?

Additionally, just because we *provide* a step, now we *require* 
division from all types (making it very cumbersome for user-defined types).


>// Pi Number Test
>auto GregorySeries = inclusiveRange!double(1, 0x1.0p+27, 2);

Very smart! ;) So, this type can support floating point values if we use 
that syntax.


Ali



Re: number ranges

2022-01-21 Thread Salih Dincer via Digitalmars-d-learn

On Thursday, 20 January 2022 at 16:33:20 UTC, Ali Çehreli wrote:


So if we add the 1.0 value after 0.900357627869 to be 
*inclusive*, then that last step would not be 0.3 anymore. 
(Thinking about it, step would mess up things for integral 
types as well; so, it must be checked during construction.)


The other obvious issue in the output is that a floating point 
iota cannot be bidirectional because the element values would 
be different.


The test that did not pass now passes. There is the issue of 
T.min being excluded from the property list for the double type. 
I tried to solve it, how is it?


Salih

```d
auto inclusiveRange(T)(T f, T l, T s = cast(T)0)
in(!isBoolean!T) {
  static assert(!isBoolean!T, "\n
  Cannot be used with bool type\n");
  if(!s) s++;
  return InclusiveRange!T(f, l, s);
}

struct InclusiveRange(T) {
  private:
T first, last;
bool empty_;

  public:
T step;

  this(U)(in U first, in U last, in U step)
  in (first <= last, format!"\n
  Invalid range:[%s,%s]."(first, last))
  {
this.first = first;
this.last = last;
this.step = step;
this.empty_ = false;
  }

  bool opBinaryRight(string op:"in")(T rhs) {
foreach(r; this) {
  if(r == rhs) return true;
}
return false;
  }

  auto save() inout { return this; }
  bool empty() inout { return empty_; }
  T front() inout { return first; }
  T back() inout { return last; }

  void popFront() {
if(!empty) {
  if(last >= first + step) {
first += step;
  } else {
empty_ = true;
if(T.max <= first + step) {
  first += step;
}
  }
}
  }

  void popBack() {
if(!empty) {
  if(first <= last-step) {
last -= step;
  } else {
empty_ = true;
if(!T.max >= last - step) {
  last -= step;
}
  }
}
  }

  size_t length() inout {
auto len = 1 + (last - first) / step;
return cast(size_t)len;
  }
}

import std.algorithm, std.math;
import std.range, std.traits;
import std.stdio, std.format, std.conv;

void main() {
  // Pi Number Test
  auto GregorySeries = inclusiveRange!double(1, 0x1.0p+27, 2);
  double piNumber = 0;
  foreach(e, n; GregorySeries.enumerate) {
if(e & 1) piNumber -= 1/n;
else piNumber += 1/n;
  }
  writefln!"%.21f (constant)"(PI);
  writefln!"%.21f (calculated)"(piNumber * 4);

} unittest {
  // Should not be possible to have an empty range

  auto r = inclusiveRange(ubyte.min, ubyte.max);
  static assert(is(ElementType!(typeof(r)) == ubyte));

  assert(r.sum == (ubyte.max * (ubyte.max + 1)) / 2);
}
```




Re: number ranges

2022-01-20 Thread Ali Çehreli via Digitalmars-d-learn

On 1/19/22 21:24, Salih Dincer wrote:

> ```d
>size_t length() inout {
>  //return last_ - first_ + 1 - empty_;/*
>  auto len = 1 + last_ - first_;
>  return cast(size_t)len;//*/
>}
> ```

Good catch but we can't ignore '- empty_'. Otherwise an empty range will 
return 1.


> But it only works on integers.

After fixing the size_t issue, it should work on user-defined types as 
well. In fact, it is better to leave the return type as auto so that it 
works with user-defined types that support the length expression but is 
a different type like e.g. MyDiffType.


Having said that, floating point types don't make sense with the 
semantics of a *bidirectional and inclusive* range. :) Let's see how it 
looks for ranges where the step size is 0.3:


import std.stdio;

void main() {
  float beg = 0.0;
  float end = 1.0;
  float step = 0.3;

  writeln("\nIncrementing:");
  for (float f = beg; f <= end; f += step) {
report(f);
  }

  writeln("\nDecrementing:");
  for (float f = end; f >= beg; f -= step) {
report(f);
  }
}

void report(float f) {
  writefln!"%.16f"(f);
}

Here is the output:

Incrementing:
0.
0.300119209290
0.600238418579
0.900357627869
<-- Where is 1.0?
Decrementing:
1.
0.699880790710
0.399761581421
0.099642372131
<-- Where is 0.0?

So if we add the 1.0 value after 0.900357627869 to be *inclusive*, 
then that last step would not be 0.3 anymore. (Thinking about it, step 
would mess up things for integral types as well; so, it must be checked 
during construction.)


The other obvious issue in the output is that a floating point iota 
cannot be bidirectional because the element values would be different.


Ali



Re: number ranges

2022-01-19 Thread Salih Dincer via Digitalmars-d-learn

Hi,

It looks so delicious.  Thank you.

On Wednesday, 19 January 2022 at 18:59:10 UTC, Ali Çehreli wrote:


And adding length() was easy as well.

Finally, I have provided property functions instead of allowing 
direct access to members.


It doesn't matter as we can't use a 3rd parameter.  But it 
doesn't work for any of these types: real, float, double.


My solution:
```d
  size_t length() inout {
//return last_ - first_ + 1 - empty_;/*
auto len = 1 + last_ - first_;
return cast(size_t)len;//*/
  }
```
But it only works on integers. In this case, we have two options! 
The first is to require the use of integers, other 3 parameter 
usage:

```d
// ...
  size_t length() inout {
auto len = 1 + (last - front) / step;
return cast(size_t)len;
  }
} unittest {
  enum { ira = 0.1,
 irb = 2.09,
 irc = 0.11
   }
  auto test = inclusiveRange(ira, irb, irc);
  assert(test.count == 19);

  auto arr = iota(ira, irb, irc).array;
  assert(test.length == arr.length);
}
```

Salih


Re: number ranges

2022-01-19 Thread Ali Çehreli via Digitalmars-d-learn

On 1/19/22 04:51, Salih Dincer wrote:

> Is it okay to swap places instead of throwing an error?

I would be happier if my potential mistake is caught instead of the 
library doing something on its own.


> Let's also
> implement BidirectionalRange, if okay.

I had started experimenting with that as well. The implementation below 
does not care if popFront() or popBack() are called on empty ranges. The 
programmer must be careful. :)


> "Does it reverse the result
> in case ```a > b``` like we
> did with foreach_reverse()"

No, it should not reverse the direction that way because we already have 
retro. :)


  inclusiveRange(1, 10).retro;

Improving the range as a BidirectionalRange requires three more 
functions: save(), back(), and popBack().


I am also removing the silly 'const' qualifiers from member functions 
because a range object is supposed to be mutated. I came to that 
conclusion after realizing that save() cannot be 'const' because it 
break isForwardRange (which is required by isBidirectionalRange). Ok, I 
will change all 'const's to 'inout's in case someone passes an object to 
a function that takes by 'const' and just applies e.g. empty() on it.


And adding length() was easy as well.

Finally, I have provided property functions instead of allowing direct 
access to members.


struct InclusiveRange(T) {
  import std.format : format;

  T first_;
  T last_;
  bool empty_;

  this(U)(in U first, in U last)
  in (first <= last, format!"Invalid range: [%s,%s]."(first, last)) {
this.first_ = first;
this.last_ = last;
this.empty_ = false;
  }

  T front() inout {
return first_;
  }

  bool empty() inout {
return empty_;
  }

  void popFront() {
if (first_ == last_) {
  empty_ = true;

} else {
  ++first_;
}
  }

  auto save() inout {
return this;
  }

  T back() inout {
return last_;
  }

  void popBack() {
if (first_ == last_) {
  empty_ = true;

} else {
  --last_;
}
  }

  size_t length() inout {
return last_ - first_ + 1 - empty_;
  }
}

auto inclusiveRange(T)(in T first, in T last) {
  return InclusiveRange!T(first, last);
}

unittest {
  // Invalid range should throw
  import std.exception : assertThrown;

  assertThrown!Error(inclusiveRange(2, 1));
}

unittest {
  // Should not be possible to have an empty range
  import std.algorithm : equal;

  auto r = inclusiveRange(42, 42);
  assert(!r.empty);
  assert(r.equal([42]));
}

unittest {
  // Should be able to represent all values of a type
  import std.range : ElementType;
  import std.algorithm : sum;

  auto r = inclusiveRange(ubyte.min, ubyte.max);
  static assert(is(ElementType!(typeof(r)) == ubyte));

  assert(r.sum == (ubyte.max * (ubyte.max + 1)) / 2);
}

unittest {
  // Should produce the last value
  import std.algorithm : sum;

  assert(inclusiveRange(1, 10).sum == 55);
}

unittest {
  // Should work with negative values
  import std.algorithm : equal;
  assert(inclusiveRange(-3, 3).equal([-3, -2, -1, 0, 1, 2, 3]));
  assert(inclusiveRange(-30, -27).equal([-30, -29, -28, -27]));
}

unittest {
  // length should be correct
  import std.range : enumerate, iota;

  enum first = 5;
  enum last = 42;
  auto r = inclusiveRange(first, last);

  // Trusting iota's implementation
  size_t expectedLength = iota(first, last).length + 1;
  size_t i = 0;
  do {
assert(r.length == expectedLength);
r.popFront();
--expectedLength;
  } while (!r.empty);
}

unittest {
  // Should provide the BidirectionalRange interface
  import std.range : retro;
  import std.algorithm : equal;

  auto r = inclusiveRange(1, 10);
  assert(!r.save.retro.equal(r.save));
  assert(r.save.retro.retro.equal(r.save));
}

void main() {
  import std.stdio;
  import std.range;

  writeln(inclusiveRange(1, 10));
  writeln(inclusiveRange(1, 10).retro);

  auto r = inclusiveRange(1, 11);
  while (true) {
writefln!"%s .. %s  length: %s"(r.front, r.back, r.length);
r.popFront();
if (r.empty) {
  break;
}
r.popBack();
if (r.empty) {
  break;
}
  }
}

Ali



Re: number ranges

2022-01-19 Thread Salih Dincer via Digitalmars-d-learn

On Tuesday, 18 January 2022 at 23:13:14 UTC, Ali Çehreli wrote:


But I like the following one better because
it is fast and I think it works correctly.


Is it okay to swap places instead of throwing an error? Let's 
also implement BidirectionalRange, if okay. This great struct 
will now run 4x4 like a Jeep. 


Moreover, the iota doesn't even care if define char type. And no 
throwing an error.


The real question to ask is:

"Does it reverse the result
in case ```a > b``` like we
did with foreach_reverse()"

Salih

```d
import std;

struct InclusiveRange(T) {
  T front, last;

  this(U)(in U front, in U last) {
this.front = front;
this.last = last;

if(empty) toogleFrontLast();
  }

  bool empty() {
return front > last;
  }

  void popFront() {
if(!empty) ++front;
  }

  T back() {
return last;
  }

  void popBack() {
if(!empty) --last;
  }

  void toogleFrontLast() {
auto temp = this.last;
this.last = this.front;
this.front = temp;
  }
}

auto inclusiveRange(T)(T first, T last) {
  return InclusiveRange!T(first, last);
}

enum a = 8; // ASCII 80: P
enum b = 7; // ASCII 70: F

alias type = char;
alias test = inclusiveRange;

void main() {
  string str; // for tests...
  auto io = iota!type(a * 10, b * 10);
   io.writeln("\n", typeof(io).stringof, "\n");

  str = to!string(io);
  assert(str == "[]"); // OMG, why?

  auto ir = test!type(a * 10, b * 10);
   ir.writeln("\n", typeof(ir).stringof, "\n");

  str = to!string(ir);
  assert(str == "FGHIJKLMNOP"); // Ok

  foreach_reverse(c; ir) str ~= c;
  assert(str == "FGHIJKLMNOPPONMLKJIHGF"); // Ok
}
```


Re: number ranges

2022-01-18 Thread forkit via Digitalmars-d-learn

On Wednesday, 19 January 2022 at 03:00:49 UTC, Tejas wrote:

On Tuesday, 18 January 2022 at 20:43:08 UTC, forkit wrote:

On Tuesday, 18 January 2022 at 16:02:42 UTC, Tejas wrote:


Newer languages nowadays use `start..intent, think it's something we should follow?


I've decided to avoid using number ranges 'directly', and 
instead use a wrapper function...



auto range(T:T)(T a, T b)
{
import std.range : iota;
return iota(a, (b+1));
}


Aww, come on; it ain't that bad...


Well that depends on entirely on what the code is doing ;-)

The key is to *always* *remember* the stop index is not included.

I sure hope they 'remembered' this in the code running on that 
telescope floating out into open space...




Re: number ranges

2022-01-18 Thread Tejas via Digitalmars-d-learn

On Tuesday, 18 January 2022 at 20:43:08 UTC, forkit wrote:

On Tuesday, 18 January 2022 at 16:02:42 UTC, Tejas wrote:


Newer languages nowadays use `start..intent, think it's something we should follow?


I've decided to avoid using number ranges 'directly', and 
instead use a wrapper function...



auto range(T:T)(T a, T b)
{
import std.range : iota;
return iota(a, (b+1));
}


Aww, come on; it ain't that bad...


Re: number ranges

2022-01-18 Thread Tejas via Digitalmars-d-learn

On Tuesday, 18 January 2022 at 17:58:54 UTC, H. S. Teoh wrote:
On Tue, Jan 18, 2022 at 04:02:42PM +, Tejas via 
Digitalmars-d-learn wrote: [...]
Newer languages nowadays use `start..intent, think it's something we should follow?


I've never seen that before.  Which languages use that?


T


In Nim for example:

```
for n in 5 .. 9:  #Both 5 and 9 are included
  echo n

echo ""

for n in 5 ..< 9: #5 is included but 9 is excluded
  echo n
```

In Odin also:
```
for i in 0..<10 {
fmt.println(i)
}
// or
for i in 0..9 {
fmt.println(i)
}
```


Re: number ranges

2022-01-18 Thread Era Scarecrow via Digitalmars-d-learn

On Monday, 17 January 2022 at 22:28:10 UTC, H. S. Teoh wrote:
This will immediately make whoever reads the code (i.e., myself 
after 2 months :D) wonder, "why +1?" And the answer will become 
clear and enlightenment ensues. ;-)


 In those cases i find myself rewriting said code. Generally to 
say **for(int i=1; i<=5; i++)** or something, where it includes 
the last one but doesn't add oddities that doesn't explain the 
magic numbers or odd +1.


 Then again the big issue *probably* comes from people coming 
from BASIC of some description where the **FOR A=1 TO 5**, where 
index starts at 1 and includes the number listed; And you aren't 
given other conditions to test against. It really does take a 
little getting used to.


 Maybe we don't use Qbasic or 8bit MSBASIC much anymore, but 
Visual Basic and legacy code grandfathers those in, and maybe a 
few other interpreted languages too.


Re: number ranges

2022-01-18 Thread Ali Çehreli via Digitalmars-d-learn

On 1/18/22 14:08, forkit wrote:

> never use number ranges.. not ever!  ;-)
>
> (except in combination with iota)

Indeed, the following is an elegant but slow (tested with dmd) 
implementation with Phobos:


auto range(T)(T a, T b)
in (a <= b) {
  import std.range : chain, iota, only;
  return chain(iota(a, b), only(b));
}

But I like the following one better because it is fast and I think it 
works correctly. However, I am reminded of one of the reasons why 
exclusive ranges are better: It is not possible to represent an empty 
range with the same syntax. For example, range(42, 42) includes 42. 
Hmmm. Should range(42, 41) mean empty? Looks weird.


struct InclusiveRange(T) {
  T front;
  T last;
  bool empty;

  this(U)(in U front, in U last)
  in (front <= last) {
this.front = front;
this.last = last;
this.empty = false;
  }

  void popFront() {
if (front == last) {
  empty = true;

} else {
  ++front;
}
  }
}

auto inclusiveRange(T)(T first, T last) {
  return InclusiveRange!T(first, last);
}

unittest {
  // Impossible to be empty
  import std.algorithm : equal;

  auto r = inclusiveRange(42, 42);
  assert(!r.empty);
  assert(r.equal([42]));
}

unittest {
  // Can represent all values of a type
  import std.range : ElementType;
  import std.algorithm : sum;

  auto r = inclusiveRange(ubyte.min, ubyte.max);
  static assert(is(ElementType!(typeof(r)) == ubyte));

  assert(r.sum == (ubyte.max * (ubyte.max + 1)) / 2);
}

unittest {
  // Really inclusive
  import std.algorithm : sum;

  assert(inclusiveRange(1, 10).sum == 55);
}

unittest {
  // Works with negative values
  import std.algorithm : equal;

  assert(inclusiveRange(-3, 3).equal([-3, -2, -1, 0, 1, 2, 3]));
  assert(inclusiveRange(-30, -27).equal([-30, -29, -28, -27]));
}

import std.stdio;

void main() {
}

Ali



Re: number ranges

2022-01-18 Thread forkit via Digitalmars-d-learn

On Tuesday, 18 January 2022 at 20:50:06 UTC, Ali Çehreli wrote:


Needs a little more work to be correct. The following produces 
and empty range. ;)


  range(uint.min, uint.max)

Also, is it important for the result to be the same as T? For 
example, even if T is ubyte, because b+1 is 'int', the range 
will produce ints.


Ali


a change of mind...

never use number ranges.. not ever!  ;-)

(except in combination with iota)



Re: number ranges

2022-01-18 Thread Ali Çehreli via Digitalmars-d-learn

On 1/18/22 12:43, forkit wrote:

> wrapper function...
>
>
> auto range(T:T)(T a, T b)
> {
>  import std.range : iota;
>  return iota(a, (b+1));
> }

Needs a little more work to be correct. The following produces and empty 
range. ;)


  range(uint.min, uint.max)

Also, is it important for the result to be the same as T? For example, 
even if T is ubyte, because b+1 is 'int', the range will produce ints.


Ali



Re: number ranges

2022-01-18 Thread forkit via Digitalmars-d-learn

On Tuesday, 18 January 2022 at 16:02:42 UTC, Tejas wrote:


Newer languages nowadays use `start..intent, think it's something we should follow?


I've decided to avoid using number ranges 'directly', and instead 
use a wrapper function...



auto range(T:T)(T a, T b)
{
import std.range : iota;
return iota(a, (b+1));
}




Re: number ranges

2022-01-18 Thread H. S. Teoh via Digitalmars-d-learn
On Tue, Jan 18, 2022 at 04:02:42PM +, Tejas via Digitalmars-d-learn wrote:
[...]
> Newer languages nowadays use `start.. it's something we should follow?

I've never seen that before.  Which languages use that?


T

-- 
"If you're arguing, you're losing." -- Mike Thomas


Re: number ranges

2022-01-18 Thread Tejas via Digitalmars-d-learn

On Monday, 17 January 2022 at 22:48:17 UTC, H. S. Teoh wrote:
On Mon, Jan 17, 2022 at 10:35:30PM +, forkit via 
Digitalmars-d-learn wrote:

On Monday, 17 January 2022 at 22:28:10 UTC, H. S. Teoh wrote:
> [...]

[...]

If I were able to write a compiler, my compiler would warn you:

"This is ill-advised and you should know better! Please 
rewrite this."


:-D  If *I* were to write a compiler, it'd come with a GC 
built-in. It'd throw up 90% of programs you feed it with the 
error "this program is garbage, please throw it away and write 
something better". :-D



T


Newer languages nowadays use `start..think it's something we should follow?


Re: number ranges

2022-01-17 Thread H. S. Teoh via Digitalmars-d-learn
On Mon, Jan 17, 2022 at 10:35:30PM +, forkit via Digitalmars-d-learn wrote:
> On Monday, 17 January 2022 at 22:28:10 UTC, H. S. Teoh wrote:
> > 
> > If I ever needed to foreach over 1-based indices, I'd write it this
> > way in order to avoid all confusion:
> > 
> > foreach (i; 1 .. 5 + 1)
> > {
> > }
> > 
> > This will immediately make whoever reads the code (i.e., myself
> > after 2 months :D) wonder, "why +1?" And the answer will become
> > clear and enlightenment ensues. ;-)
[...]
> If I were able to write a compiler, my compiler would warn you:
> 
> "This is ill-advised and you should know better! Please rewrite this."

:-D  If *I* were to write a compiler, it'd come with a GC built-in. It'd
throw up 90% of programs you feed it with the error "this program is
garbage, please throw it away and write something better". :-D


T

-- 
If blunt statements had a point, they wouldn't be blunt...


Re: number ranges

2022-01-17 Thread forkit via Digitalmars-d-learn

On Monday, 17 January 2022 at 22:28:10 UTC, H. S. Teoh wrote:


If I ever needed to foreach over 1-based indices, I'd write it 
this way in order to avoid all confusion:


foreach (i; 1 .. 5 + 1)
{
}

This will immediately make whoever reads the code (i.e., myself 
after 2 months :D) wonder, "why +1?" And the answer will become 
clear and enlightenment ensues. ;-)



T


If I were able to write a compiler, my compiler would warn you:

"This is ill-advised and you should know better! Please rewrite 
this."




Re: number ranges

2022-01-17 Thread H. S. Teoh via Digitalmars-d-learn
On Mon, Jan 17, 2022 at 10:22:19PM +, forkit via Digitalmars-d-learn wrote:
[...]
> I think it's fair to say, that I'm familiar with 0-based indexing ;-)
> 
> my concern was with the 1..5 itself.
> 
> In terms of what makes sense, it actually makes more sense not to use
> it, at all ;-)

If I ever needed to foreach over 1-based indices, I'd write it this way
in order to avoid all confusion:

foreach (i; 1 .. 5 + 1)
{
}

This will immediately make whoever reads the code (i.e., myself after 2
months :D) wonder, "why +1?" And the answer will become clear and
enlightenment ensues. ;-)


T

-- 
Change is inevitable, except from a vending machine.


Re: number ranges

2022-01-17 Thread forkit via Digitalmars-d-learn

On Monday, 17 January 2022 at 22:06:47 UTC, H. S. Teoh wrote:


Basically,

foreach (i; a .. b)

is equivalent to:

for (auto i = a; i < b; i++)

Just think of that way and it will make sense.



I think it's fair to say, that I'm familiar with 0-based indexing 
;-)


my concern was with the 1..5 itself.

In terms of what makes sense, it actually makes more sense not to 
use it, at all ;-)




Re: number ranges

2022-01-17 Thread H. S. Teoh via Digitalmars-d-learn
On Mon, Jan 17, 2022 at 09:37:31PM +, forkit via Digitalmars-d-learn wrote:
> On Monday, 17 January 2022 at 11:58:18 UTC, Paul Backus wrote:
> > 
> > This kind of half-open interval, which includes the lower bound but
> > excludes the upper bound, is used in programming because it lets you
> > write
> > 
> > foreach (i; 0 .. array.length) writef("%s ", array[i]);
> > 
> > ...without going past the end of the array.
> 
> Yes. But the intent here is clearly stated and cannot be misunderstood
> -> array.length
> 
> Whereas 1..5 is just an opportunity to shoot yourself in the foot.

The compiler cannot discern intent. Both `5` and `array.length` are
expressions, as far as the compiler is concerned. So is `5 +
(array.length - sin(x))/2*exp(array2.length)`, for that matter.  The
compiler does not understand what the programmer may have intended; it
simply follows what the spec says.

Of course, to a *human* the semantics of `1..5` can be totally confusing
if you're not used to it.  The bottom-line is, in D (and in other C-like
languages) you just have to get used to 0-based indexing, because
ultimately it actually makes more sense than the 1-based counting scheme
we were taught in school. 1-based counting schemes are full of
exceptions and off-by-1 errors; it's needlessly complex and hard for the
mortal brain to keep track of all the places where you have to add or
subtract 1.  Whereas in 0-based index schemes, you *always* count from
0, and you always use `<` to check your bounds, and you can do
arithmetic with indices just by adding and subtracting as usual, without
off-by-1 errors.

Basically,

foreach (i; a .. b)

is equivalent to:

for (auto i = a; i < b; i++)

Just think of that way and it will make sense.

And never ever write 1..n unless you actually intend to skip the first
element. Remember: 0-based counting, not 1-based counting. You always
start from 0, and count up to (but not including) n.  Which also means
you should always write `<`, never write `<=`. So your upper bound is
always the element *past* the last one. I.e., it's the index at which a
new element would be added if you were appending to your list. I.e., the
index at which a new element should be added is simply array.length (not
array.length+1 or array.length-1 or any of that error-prone crap that
nobody can remember).

If you adhere to these simple rules, you'll never need to add or
subtract 1 to your counters, loop indices, and lengths (because nobody
can remember when to do that, so not having to do it significantly
reduces the chances of bugs).


T

-- 
People say I'm arrogant, and I'm proud of it.


Re: number ranges

2022-01-17 Thread forkit via Digitalmars-d-learn

On Monday, 17 January 2022 at 11:58:18 UTC, Paul Backus wrote:


This kind of half-open interval, which includes the lower bound 
but excludes the upper bound, is used in programming because it 
lets you write


foreach (i; 0 .. array.length) writef("%s ", array[i]);

...without going past the end of the array.


Yes. But the intent here is clearly stated and cannot be 
misunderstood -> array.length


Whereas 1..5 is just an opportunity to shoot yourself in the foot.

"the heretic must be cast out not because of the probability that 
he is wrong but because of the possibility that he is right." - 
Edsger W. Dijkstra


Re: number ranges

2022-01-17 Thread Salih Dincer via Digitalmars-d-learn

On Monday, 17 January 2022 at 11:58:18 UTC, Paul Backus wrote:

On Monday, 17 January 2022 at 10:24:06 UTC, forkit wrote:
Edsger W. Dijkstra, a well-known academic computer scientist, 
has written in more detail about the advantages of this kind of 
interval: 
https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html


Thank you for this valuable information you have given. There is 
a nice feature used in uniform():

```d
import std;

enum a = 9;
enum b = 10;

void main() {
  auto sonYok = generate!(() => uniform!"[)"(a, b)).take(10);
   sonYok.writeln; // only 9 (default)

  auto ilk_son = generate!(() => uniform!"[]"(a, b)).take(10);
   ilk_son.writeln; // may contain 9 & 10

  auto orta = generate!(() => uniform!"()"(a, b + 1)).take(10);
   orta.writeln; // only 10

  auto ilkYok = generate!(() => uniform!"(]"(a, b + 1)).take(10);
   ilkYok.writeln; // Does not contain 9
}
```
It would be nice if this feature, which we set up with templates, 
could be applied everywhere in D. Because sometimes it is needed.


As for other, I never used this feature until I got used to it. 
Of course, it's practical like this, it will do 10 reps:


```foreach(_;0..11)```

Salih


Re: number ranges

2022-01-17 Thread Paul Backus via Digitalmars-d-learn

On Monday, 17 January 2022 at 10:24:06 UTC, forkit wrote:

so I'm wondering why the code below prints:

1 2 3 4

and not

1 2 3 4 5

as I would expect.

foreach (value; 1..5) writef("%s ", value);


This kind of half-open interval, which includes the lower bound 
but excludes the upper bound, is used in programming because it 
lets you write


foreach (i; 0 .. array.length) writef("%s ", array[i]);

...without going past the end of the array.

Edsger W. Dijkstra, a well-known academic computer scientist, has 
written in more detail about the advantages of this kind of 
interval: 
https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html



also, why is this not possible:

int[] arr = 1..5.array;


The `lower .. upper` syntax only works in foreach loops. If you 
want to create a range of numbers like this in another context, 
you must use the library function std.range.iota:


import std.range: iota;
int[] arr = iota(1, 5).array;

(Why "iota"? Because in APL, the Greek letter iota (ι) is used to 
create a range of numbers like this.)


number ranges

2022-01-17 Thread forkit via Digitalmars-d-learn

so I'm wondering why the code below prints:

1 2 3 4

and not

1 2 3 4 5

as I would expect.

foreach (value; 1..5) writef("%s ", value);

also, why is this not possible:

int[] arr = 1..5.array;




Re: bool empty() const for ranges

2021-11-26 Thread Steven Schveighoffer via Digitalmars-d-learn

On 11/26/21 5:44 AM, Salih Dincer wrote:

Hi All;

I have two questions that make each other redundant. Please answer one 
of them. I'm implementing ```bool empty() const``` for ranges as below:


```d
   bool empty() // const
   {
     bool result;

     if(!head)
     {
   result = true;
   fastRewind();
     }
     return result; // head ? false : true;
   }
```
* Is the const essential for ranges?


No, there is no specification of whether any range methods have to be 
const. As Stanislav says, it is only a requirement that subsequent calls 
return the same value as long as popFront has not been called.


* Is it possible to rewind the pointer (```Node * head;```) when my head 
is empty by the const?


If const is set, then any members are treated as const, and anything 
they point to are treated as const. So no.


That being said, if `fastRewind` changes `empty` from true to false, the 
method is invalid.


-Steve


Re: bool empty() const for ranges

2021-11-26 Thread Stanislav Blinov via Digitalmars-d-learn

On Friday, 26 November 2021 at 10:44:10 UTC, Salih Dincer wrote:


* Is the const essential for ranges?
* Is it possible to rewind the pointer (```Node * head;```) 
when my head is empty by the const?


`empty` is not required to be `const`, but it is required to 
yield the same result if called multiple times without mutating 
the range (see 
https://dlang.org/phobos/std_range_primitives.html#.isInputRange). In other words, you really ought not to mutate the range in implementation of `empty`, or at least not to the extent you seem to want to.


bool empty() const for ranges

2021-11-26 Thread Salih Dincer via Digitalmars-d-learn

Hi All;

I have two questions that make each other redundant. Please 
answer one of them. I'm implementing ```bool empty() const``` for 
ranges as below:


```d
  bool empty() // const
  {
bool result;

if(!head)
{
  result = true;
  fastRewind();
}
return result; // head ? false : true;
  }
```
* Is the const essential for ranges?
* Is it possible to rewind the pointer (```Node * head;```) when 
my head is empty by the const?


Thanks...


Re: A little help with Ranges

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

On 8/27/21 12:41 AM, Merlin Diavova wrote:

On Friday, 27 August 2021 at 04:01:19 UTC, Ali Çehreli wrote:

On 8/26/21 7:17 PM, Merlin Diavova wrote:


[...]


Then the operations downstream will not produce any results. For 
example, the array will be empty below:


import std.stdio;
import std.range;
import std.algorithm;
import std.string;
import std.functional;

void main() {
  auto significantLines = stdin
  .byLineCopy
  .map!strip
  .filter!(not!empty)
  .filter!(line => line.front != '#')
  .array;

  if (significantLines.empty) {
    writeln("There were no significant lines.");

  } else {
    writefln!"The lines: %-(\n%s%)"(significantLines);
  }
}

Ali


And there it is!

I was missing

```d
.filter!(not!empty)
```

My code now works exactly how I wanted. Thanks!


Be careful with this! `not!empty` is *only* working because you are 
using arrays (where `empty` is a UFCS function defined in std.range). 
Other ranges this will not work on. Instead, I would recommend a lambda 
(which will work with arrays too):


```d
.filter!(r => !r.empty)
```

-Steve


Re: A little help with Ranges

2021-08-26 Thread Merlin Diavova via Digitalmars-d-learn

On Friday, 27 August 2021 at 04:01:19 UTC, Ali Çehreli wrote:

On 8/26/21 7:17 PM, Merlin Diavova wrote:


[...]


Then the operations downstream will not produce any results. 
For example, the array will be empty below:


import std.stdio;
import std.range;
import std.algorithm;
import std.string;
import std.functional;

void main() {
  auto significantLines = stdin
  .byLineCopy
  .map!strip
  .filter!(not!empty)
  .filter!(line => line.front != '#')
  .array;

  if (significantLines.empty) {
writeln("There were no significant lines.");

  } else {
writefln!"The lines: %-(\n%s%)"(significantLines);
  }
}

Ali


And there it is!

I was missing

```d
.filter!(not!empty)
```

My code now works exactly how I wanted. Thanks!


Re: A little help with Ranges

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

On 8/26/21 7:17 PM, Merlin Diavova wrote:

What I meant about the handling an empty filter is, what if I want to 
take an alternative route if the filter returns empty?


Then the operations downstream will not produce any results. For 
example, the array will be empty below:


import std.stdio;
import std.range;
import std.algorithm;
import std.string;
import std.functional;

void main() {
  auto significantLines = stdin
  .byLineCopy
  .map!strip
  .filter!(not!empty)
  .filter!(line => line.front != '#')
  .array;

  if (significantLines.empty) {
writeln("There were no significant lines.");

  } else {
writefln!"The lines: %-(\n%s%)"(significantLines);
  }
}

Ali


Re: A little help with Ranges

2021-08-26 Thread Stefan Koch via Digitalmars-d-learn

On Friday, 27 August 2021 at 02:17:21 UTC, Merlin Diavova wrote:

On Friday, 27 August 2021 at 02:10:48 UTC, Stefan Koch wrote:
On Friday, 27 August 2021 at 01:51:42 UTC, Merlin Diavova 
wrote:

Hi all,

I'm Merlin, I'm just starting out in D and super excited.

My questions are:-

1. In a range pipeline how does one handle the event of a 
filter range returning empty?


2. How does one unwrap a single result from a range operation?


Look forward to your assistance!

Merlin


1. you don't have to handle it.
it just won't go.

2. `takeOne` put it into the search on the dlang site


Thanks for the quick response!

Took a look at takeOne and yep that's the ticket.

What I meant about the handling an empty filter is, what if I 
want to take an alternative route if the filter returns empty?


You check if it's by calling empty.
or do you want a default value? I am sure there is a convince 
function for that somewhere in phobos.


Re: A little help with Ranges

2021-08-26 Thread Merlin Diavova via Digitalmars-d-learn

On Friday, 27 August 2021 at 02:10:48 UTC, Stefan Koch wrote:

On Friday, 27 August 2021 at 01:51:42 UTC, Merlin Diavova wrote:

Hi all,

I'm Merlin, I'm just starting out in D and super excited.

My questions are:-

1. In a range pipeline how does one handle the event of a 
filter range returning empty?


2. How does one unwrap a single result from a range operation?


Look forward to your assistance!

Merlin


1. you don't have to handle it.
it just won't go.

2. `takeOne` put it into the search on the dlang site


Thanks for the quick response!

Took a look at takeOne and yep that's the ticket.

What I meant about the handling an empty filter is, what if I 
want to take an alternative route if the filter returns empty?


Re: A little help with Ranges

2021-08-26 Thread Stefan Koch via Digitalmars-d-learn

On Friday, 27 August 2021 at 01:51:42 UTC, Merlin Diavova wrote:

Hi all,

I'm Merlin, I'm just starting out in D and super excited.

My questions are:-

1. In a range pipeline how does one handle the event of a 
filter range returning empty?


2. How does one unwrap a single result from a range operation?


Look forward to your assistance!

Merlin


1. you don't have to handle it.
it just won't go.

2. `takeOne` put it into the search on the dlang site


A little help with Ranges

2021-08-26 Thread Merlin Diavova via Digitalmars-d-learn

Hi all,

I'm Merlin, I'm just starting out in D and super excited.

My questions are:-

1. In a range pipeline how does one handle the event of a filter 
range returning empty?


2. How does one unwrap a single result from a range operation?


Look forward to your assistance!

Merlin


Re: foreach() behavior on ranges

2021-08-26 Thread frame via Digitalmars-d-learn
On Wednesday, 25 August 2021 at 11:02:23 UTC, Steven 
Schveighoffer wrote:

On 8/25/21 4:31 AM, frame wrote:
On Tuesday, 24 August 2021 at 21:15:02 UTC, Steven 
Schveighoffer wrote:
I'm surprised you bring PHP as an example, as it appears 
their foreach interface works EXACTLY as D does:


Yeah, but the point is, there is a rewind() method. That is 
called every time on foreach().


It seems what you are after is forward ranges. Those are able 
to "rewind" when you are done with them. It's just not done 
through a rewind method, but via saving the range before 
iteration:


```d
foreach(val; forwardRange.save)
{
   ...
   break;
}

// forwardRange hasn't been iterated here
```

-Steve


This could be any custom method for my ranges or forward range 
returned by some function.


But that doesn't help if some thirdparty library function would 
break and return just an input range. Then it seems that it must 
be very properly implemented like postblit technics mentioned 
before. Some author may never care about.


That it works in 99% of all cases should not be an excuse for a 
design flaw.

The documentation really need to mention this.





Re: foreach() behavior on ranges

2021-08-26 Thread Joseph Rushton Wakeling via Digitalmars-d-learn

On Wednesday, 25 August 2021 at 19:51:36 UTC, H. S. Teoh wrote:
What I understand from what Andrei has said in the past, is 
that a range is merely a "view" into some underlying storage; 
it is not responsible for the contents of that storage.  My 
interpretation of this is that .save will only save the 
*position* of the range, but it will not save the contents it 
points to, so it will not (should not) deep-copy.


That definition is potentially misleading if we take into account 
that a range is not necessarily iterating over some underlying 
storage: ranges can also be defined by algorithmic processes.  
(Think e.g. iota, or pseudo-RNGs, or a range that iterates over 
the Fibonacci numbers.)


However, if the range is implemented by a struct that contains 
a reference to its iteration state, then yes, to satisfy the 
definition of .save it should deep-copy this state.


Right.  And in the case of algorithmic ranges (rather than 
container-derived ranges), the state is always and only the 
iteration state.  And then as well as that there are ranges that 
are iterating over external IO, which in most cases can't be 
treated as forward ranges but in a few cases might be (e.g. 
saving the cursor position when iterating over a file's contents).


Arguably I think a lot of problems in the range design derive 
from not thinking through those distinctions in detail 
(external-IO-based vs. algorithmic vs. container-based), even 
though superficially those seem to map well to the input vs 
forward vs bidirectional vs random-access range distinctions.


That's also not taking into account edge cases, e.g. stuff like 
RandomShuffle or RandomSample: here one can in theory copy the 
"head" of the range but one arguably wants to avoid correlations 
in the output of the different copies (which can arise from at 
least 2 different sources: copying under-the-hood pseudo-random 
state of the sampling/shuffling algorithm itself, or copying the 
underlying pseudo-random number generator).  Except perhaps in 
the case where one wants to take advantage of the pseudo-random 
feature to reproduce those sequences ... but then one wants that 
to be a conscious programmer decision, not happening by accident 
under the hood of some library function.


(Rabbit hole, here we come.)

Andrei has mentioned before that in retrospect, .save was a 
design mistake.  The difference between an input range and a 
forward range should have been keyed on whether the range type 
has reference semantics (input range) or by-value semantics 
(forward range).  But for various reasons, including the state 
of the language at the time the range API was designed, the 
.save route was chosen, and we're stuck with it unless Phobos 
2.0 comes into existence.


Either way, though, the semantics of a forward range pretty 
much dictates that whatever type a range has, if it claims to 
be a forward range then .save must preserve whatever iteration 
state it has at that point in time. If this requires 
deep-copying some state referenced from a struct, then that's 
what it takes to satisfy the API.  This may take the form of a 
.save method that copies state, or a copy ctor that does the 
same, or simply storing iteration state as PODs in the range 
struct so that copying the struct equates to preserving the 
iteration state.


Yes.  FWIW I agree that when _implementing_ a forward range one 
should probably make sure that copying by value and the `save` 
method produce the same results.


But as a _user_ of code implemented using the current range API, 
it might be a bad idea to assume that a 3rd party forward range 
implementation will necessarily guarantee that.


Re: foreach() behavior on ranges

2021-08-25 Thread H. S. Teoh via Digitalmars-d-learn
On Wed, Aug 25, 2021 at 04:46:54PM +, Joseph Rushton Wakeling via 
Digitalmars-d-learn wrote:
> On Wednesday, 25 August 2021 at 10:59:44 UTC, Steven Schveighoffer wrote:
> > structs still provide a mechanism (postblit/copy ctor) to properly
> > save a forward range when copying, even if the guts need copying
> > (unlike classes). In general, I think it was a mistake to use
> > `.save` as the mechanism, as generally `.save` is equivalent to
> > copying, so nobody does it, and code works fine for most ranges.
> 
> Consider a struct whose internal fields are just a pointer to its
> "true" internal state.  Does one have any right to assume that the
> postblit/copy ctor would necessarily deep-copy that?
[...]
> If that struct implements a forward range, though, and that pointed-to
> state is mutated by iteration of the range, then it would be
> reasonable to assume that the `save` method MUST deep-copy it, because
> otherwise the forward-range property would not be respected.
[...]

What I understand from what Andrei has said in the past, is that a range
is merely a "view" into some underlying storage; it is not responsible
for the contents of that storage.  My interpretation of this is that
.save will only save the *position* of the range, but it will not save
the contents it points to, so it will not (should not) deep-copy.

However, if the range is implemented by a struct that contains a
reference to its iteration state, then yes, to satisfy the definition of
.save it should deep-copy this state.


> With that in mind, I am not sure it's reasonable to assume that just
> because a struct implements a forward-range API, that copying the
> struct instance is necessarily the same as saving the range.
[...]

Andrei has mentioned before that in retrospect, .save was a design
mistake.  The difference between an input range and a forward range
should have been keyed on whether the range type has reference semantics
(input range) or by-value semantics (forward range).  But for various
reasons, including the state of the language at the time the range API
was designed, the .save route was chosen, and we're stuck with it unless
Phobos 2.0 comes into existence.

Either way, though, the semantics of a forward range pretty much
dictates that whatever type a range has, if it claims to be a forward
range then .save must preserve whatever iteration state it has at that
point in time. If this requires deep-copying some state referenced from
a struct, then that's what it takes to satisfy the API.  This may take
the form of a .save method that copies state, or a copy ctor that does
the same, or simply storing iteration state as PODs in the range struct
so that copying the struct equates to preserving the iteration state.


T

-- 
Why waste time reinventing the wheel, when you could be reinventing the engine? 
-- Damian Conway


Re: foreach() behavior on ranges

2021-08-25 Thread Joseph Rushton Wakeling via Digitalmars-d-learn
On Wednesday, 25 August 2021 at 17:01:54 UTC, Steven 
Schveighoffer wrote:
In a world where copyability means it's a forward range? Yes. 
We aren't in that world, it's a hypothetical "if we could go 
back and redesign".


OK, that makes sense.

Technically this is true. In practice, it rarely happens. The 
flaw of `save` isn't that it's an unsound API, the flaw is that 
people get away with just copying, and it works 99.9% of the 
time. So code is simply untested with ranges where `save` is 
important.


This is very true, and makes it quite reasonable to try to pursue 
"the obvious/lazy thing == the thing you're supposed to do" 
w.r.t. how ranges are defined.


I'd be willing to bet $10 there is a function in phobos right 
now, that takes forward ranges, and forgets to call `save` when 
iterating with foreach. It's just so easy to do, and works with 
most ranges in existence.


I'm sure you'd win that bet!


The idea is to make the meaning of a range copy not ambiguous.


Yes, this feels reasonable.  And then one can reserve the idea of 
a magic deep-copy method for special cases like pseudo-RNGs where 
one wants them to be copyable on user request, but without code 
assuming it can copy them.


Re: foreach() behavior on ranges

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

On 8/25/21 12:46 PM, Joseph Rushton Wakeling wrote:

On Wednesday, 25 August 2021 at 10:59:44 UTC, Steven Schveighoffer wrote:
structs still provide a mechanism (postblit/copy ctor) to properly 
save a forward range when copying, even if the guts need copying 
(unlike classes). In general, I think it was a mistake to use `.save` 
as the mechanism, as generally `.save` is equivalent to copying, so 
nobody does it, and code works fine for most ranges.


Consider a struct whose internal fields are just a pointer to its "true" 
internal state.  Does one have any right to assume that the 
postblit/copy ctor would necessarily deep-copy that?


In a world where copyability means it's a forward range? Yes. We aren't 
in that world, it's a hypothetical "if we could go back and redesign".


If that struct implements a forward range, though, and that pointed-to 
state is mutated by iteration of the range, then it would be reasonable 
to assume that the `save` method MUST deep-copy it, because otherwise 
the forward-range property would not be respected.


With that in mind, I am not sure it's reasonable to assume that just 
because a struct implements a forward-range API, that copying the struct 
instance is necessarily the same as saving the range.


Technically this is true. In practice, it rarely happens. The flaw of 
`save` isn't that it's an unsound API, the flaw is that people get away 
with just copying, and it works 99.9% of the time. So code is simply 
untested with ranges where `save` is important.


Indeed, IIRC quite a few Phobos library functions program defensively 
against that difference by taking a `.save` copy of their input before 
iterating over it.


I'd be willing to bet $10 there is a function in phobos right now, that 
takes forward ranges, and forgets to call `save` when iterating with 
foreach. It's just so easy to do, and works with most ranges in existence.




What should have happened is that input-only ranges should not have 
been copyable, and copying should have been the save mechanism. Then 
it becomes way way more obvious what is happening. Yes, this means 
forgoing classes as ranges.


I think there's a benefit of a method whose definition is explicitly "If 
you call this, you will get a copy of the range which will replay 
exactly the same results when iterating over it".  Just because the 
meaning of "copy" can be ambiguous, whereas a promise about how 
iteration can be used is not.


The idea is to make the meaning of a range copy not ambiguous.

-Steve


Re: foreach() behavior on ranges

2021-08-25 Thread Joseph Rushton Wakeling via Digitalmars-d-learn
On Wednesday, 25 August 2021 at 10:59:44 UTC, Steven 
Schveighoffer wrote:
structs still provide a mechanism (postblit/copy ctor) to 
properly save a forward range when copying, even if the guts 
need copying (unlike classes). In general, I think it was a 
mistake to use `.save` as the mechanism, as generally `.save` 
is equivalent to copying, so nobody does it, and code works 
fine for most ranges.


Consider a struct whose internal fields are just a pointer to its 
"true" internal state.  Does one have any right to assume that 
the postblit/copy ctor would necessarily deep-copy that?


If that struct implements a forward range, though, and that 
pointed-to state is mutated by iteration of the range, then it 
would be reasonable to assume that the `save` method MUST 
deep-copy it, because otherwise the forward-range property would 
not be respected.


With that in mind, I am not sure it's reasonable to assume that 
just because a struct implements a forward-range API, that 
copying the struct instance is necessarily the same as saving the 
range.


Indeed, IIRC quite a few Phobos library functions program 
defensively against that difference by taking a `.save` copy of 
their input before iterating over it.


What should have happened is that input-only ranges should not 
have been copyable, and copying should have been the save 
mechanism. Then it becomes way way more obvious what is 
happening. Yes, this means forgoing classes as ranges.


I think there's a benefit of a method whose definition is 
explicitly "If you call this, you will get a copy of the range 
which will replay exactly the same results when iterating over 
it".  Just because the meaning of "copy" can be ambiguous, 
whereas a promise about how iteration can be used is not.


  1   2   3   4   5   6   7   8   9   10   >