Thread overview
chunkBy bug?
Sep 05
JG
Sep 05
JG
Sep 05
JG
Sep 06
Seb
Sep 07
JG
September 05
The following code won't compile:
----
import std.algorithm;
import std.range;
import std.stdio;

void main() {
 auto v = [2,4,8,3,6,9,1,5,7];
 foreach(i; 2..4) {
  writeln(v.myChunkBy!((a,b)=>a%i==b%i));
 }
}
----
/opt/local/include/phobos/std/algorithm/iteration.d(2009): Error: function tmp.main.ChunkByImpl!(__lambda1, int[]).ChunkByImpl.Group.popFront cannot access frame of function D main
----

Is this expected behavior?

I can get it to run as expected by modifying the source of chunkBy as follows:

----

import std.range;
import std.stdio;
import std.traits;
import std.functional;
private template ChunkByImplIsUnary(alias pred, Range)
{
 alias e = lvalueOf!(ElementType!Range);

 static if (is(typeof(binaryFun!pred(e, e)) : bool))
                                              enum ChunkByImplIsUnary = false;
 else static if (is(typeof(unaryFun!pred(e) == unaryFun!pred(e)) : bool))
                                                                  
 enum ChunkByImplIsUnary = true;
 else
  static assert(0, "chunkBy expects either a binary predicate or "~
    "a unary predicate on range elements of type: "~
    ElementType!Range.stringof);
}

// Outer range
struct Impl(Range)
{
 size_t groupNum;
 Range  current;
 Range  next;
}
// Inner range
struct Group(alias eq, Range)
{
 import std.typecons : RefCounted;
 private size_t groupNum;
 private Range  start;
 private Range  current;

 private RefCounted!(Impl!Range) mothership;

 this(RefCounted!(Impl!Range) origin)
 {
  groupNum = origin.groupNum;

  start = origin.current.save;
  current = origin.current.save;
  assert(!start.empty, "Passed range 'r' must not be empty");

  mothership = origin;

  // Note: this requires reflexivity.
  assert(eq(start.front, current.front),
    "predicate is not reflexive");
 }

 @property bool empty() { return groupNum == size_t.max; }
 @property auto ref front() { return current.front; }

 void popFront()
 {
  current.popFront();

  // Note: this requires transitivity.
  if (current.empty || !eq(start.front, current.front))
  {
   if (groupNum == mothership.groupNum)
   {
    // If parent range hasn't moved on yet, help it along by
    // saving location of start of next Group.
    mothership.next = current.save;
   }

   groupNum = size_t.max;
  }
 }

 @property auto save()
 {
  auto copy = this;
  copy.current = current.save;
  return copy;
 }
}

private struct ChunkByImpl(alias pred, Range)
if (isForwardRange!Range)
{
 import std.typecons : RefCounted;

 enum bool isUnary = ChunkByImplIsUnary!(pred, Range);

 static if (isUnary)
  alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
 else
  alias eq = binaryFun!pred;

 static assert(isForwardRange!(Group!(eq,Range)));

 private RefCounted!(Impl!Range) impl;

 this(Range r)
 {
  impl = RefCounted!(Impl!Range)(0, r, r.save);
 }

 @property bool empty() { return impl.current.empty; }

 @property auto front()
 {
  static if (isUnary)
  {
   import std.typecons : tuple;
   return tuple(unaryFun!pred(impl.current.front), Group(impl));
  }
  else
  {
   return Group!(eq,Range)(impl);
  }
 }

 void popFront()
 {
  // Scan for next group. If we're lucky, one of our Groups would have
  // already set .next to the start of the next group, in which case the
  // loop is skipped.
  while (!impl.next.empty && eq(impl.current.front, impl.next.front))
  {
   impl.next.popFront();
  }

  impl.current = impl.next.save;

  // Indicate to any remaining Groups that we have moved on.
  impl.groupNum++;
 }

 @property auto save()
 {
  // Note: the new copy of the range will be detached from any existing
  // satellite Groups, and will not benefit from the .next acceleration.
  return typeof(this)(impl.current.save);
 }

 static assert(isForwardRange!(typeof(this)), typeof(this).stringof
   ~ " must be a forward range");
}

auto chunkBy(alias pred, Range)(Range r)
 if (isInputRange!Range)
{
     return ChunkByImpl!(pred, Range)(r);
}

void main() {
 auto v = [2,4,8,3,6,9,1,5,7];
 foreach(i; 2..4) {
  writeln(v.chunkBy!((a,b)=>a%i==b%i));
 }
}
---
September 05
Sorry for posting to general. I meant to post to learn.
September 04
On Sat, Sep 05, 2020 at 03:30:33AM +0000, JG via Digitalmars-d wrote:
> The following code won't compile:
> ----
> import std.algorithm;
> import std.range;
> import std.stdio;
> 
> void main() {
>  auto v = [2,4,8,3,6,9,1,5,7];
>  foreach(i; 2..4) {
>   writeln(v.myChunkBy!((a,b)=>a%i==b%i));
>  }
> }
> ----
> /opt/local/include/phobos/std/algorithm/iteration.d(2009): Error: function
> tmp.main.ChunkByImpl!(__lambda1, int[]).ChunkByImpl.Group.popFront cannot
> access frame of function D main
> ----
[...]

Are you using LDC?  IIRC there was a recent bug fixed in dmd that fixes this problem in some cases, but LDC still has this bug.  Maybe try with dmd to see if it works?


--T
September 05
On Saturday, 5 September 2020 at 04:10:58 UTC, H. S. Teoh wrote:
> On Sat, Sep 05, 2020 at 03:30:33AM +0000, JG via Digitalmars-d wrote:
>> The following code won't compile:
>> ----
>> import std.algorithm;
>> import std.range;
>> import std.stdio;
>> 
>> void main() {
>>  auto v = [2,4,8,3,6,9,1,5,7];
>>  foreach(i; 2..4) {
>>   writeln(v.myChunkBy!((a,b)=>a%i==b%i));
>>  }
>> }
>> ----
>> /opt/local/include/phobos/std/algorithm/iteration.d(2009): Error: function
>> tmp.main.ChunkByImpl!(__lambda1, int[]).ChunkByImpl.Group.popFront cannot
>> access frame of function D main
>> ----
> [...]
>
> Are you using LDC?  IIRC there was a recent bug fixed in dmd that fixes this problem in some cases, but LDC still has this bug.  Maybe try with dmd to see if it works?
>
>
> --T

Thanks for your reply. I was getting the same error with both dmd and ldc, I will try updating. For clarity do you know what precisely is causing the problem? My "fix" was to un-nest the structs used in chunkBy. Was the bug in dmd, something to do with nested structs?

September 06
On Sat, Sep 05, 2020 at 11:14:08AM +0000, JG via Digitalmars-d wrote: [...]
> Thanks for your reply. I was getting the same error with both dmd and ldc, I will try updating. For clarity do you know what precisely is causing the problem? My "fix" was to un-nest the structs used in chunkBy. Was the bug in dmd, something to do with nested structs?

The problem was caused by nested structs needing a context pointer, and an alias function parameter also needing a context pointer. Older versions of dmd could only handle 1 context pointer at a time, so was unable to handle this case.


T

-- 
Some ideas are so stupid that only intellectuals could believe them. -- George Orwell
September 06
On Saturday, 5 September 2020 at 03:30:33 UTC, JG wrote:
> The following code won't compile:
> ----
> import std.algorithm;
> import std.range;
> import std.stdio;
>
> void main() {
>  auto v = [2,4,8,3,6,9,1,5,7];
>  foreach(i; 2..4) {
>   writeln(v.myChunkBy!((a,b)=>a%i==b%i));
>  }
> }
> ----
> /opt/local/include/phobos/std/algorithm/iteration.d(2009): Error: function tmp.main.ChunkByImpl!(__lambda1, int[]).ChunkByImpl.Group.popFront cannot access frame of function D main
> ----
>
> Is this expected behavior?

No.

> I can get it to run as expected by modifying the source of chunkBy as follows:

It's hard to read this and compare against the original implementation here.
If this issue still persists with master, why don't you simply open a PR for Phobos?
Bug fixes are always welcome!
September 07
On Sunday, 6 September 2020 at 17:08:06 UTC, Seb wrote:

> It's hard to read this and compare against the original implementation here.
> If this issue still persists with master, why don't you simply open a PR for Phobos?
> Bug fixes are always welcome!

I compiled dmd and phobos from source. Using the same code as before:
----
import std.algorithm;
import std.range;
import std.stdio;

void main() {
 auto v = [2,4,8,3,6,9,1,5,7];
 foreach(i; 2..4) {
  writeln(v.myChunkBy!((a,b)=>a%i==b%i));
 }
}
----
and  compiling with the new version of dmd produces:

../../dlang/dmd/generated/osx/release/64/../../../../../phobos/std/algorithm/iteration.d(2058): Error: function chunkByFix.main.ChunkByImpl!(__lambda1, int[]).ChunkByImpl.Group.popFront cannot access delegate __lambda1 in frame of function D main

I will see if I can figure out how to attempt a PR for phobos.