Jump to page: 1 2
Thread overview
Problem with closures
Jan 11, 2007
Alain
Jan 11, 2007
Frits van Bommel
Jan 11, 2007
Alain
Jan 11, 2007
Lutger
Jan 12, 2007
Bradley Smith
Jan 12, 2007
Bradley Smith
Jan 12, 2007
Frits van Bommel
Jan 11, 2007
David Medlock
Re: Problem with closures: Problem solved
Jan 11, 2007
Alain
Jan 11, 2007
Bradley Smith
Jan 11, 2007
Daniel Giddings
Jan 12, 2007
Bradley Smith
Jan 12, 2007
Daniel Giddings
January 11, 2007
Hello,

I am experimenting with closures and i have problem with the following code:

import std.stdio;
alias int delegate(int x) AddFunc;

AddFunc addN(int n) {
    int add(int i) {
        return i + n;
    }
    return &add; // the add function captured will remember the value of n
}

void apply(int[] array, AddFunc func) {
    foreach (inout int i; array) {
        i = func(i);
    }
}

int main() {
    int[] numbers = [1,2,3];
    writefln("numbers before=",numbers);
    apply(numbers, addN(40)); // add 40 to each number
    writefln("numbers after=",numbers);
    return 0;
}

The result should be [41,42,43] but i get [4202694,4202695,4202696]

Am i missing something?

Alain
January 11, 2007
Alain wrote:
> AddFunc addN(int n) {
>     int add(int i) {
>         return i + n;
>     }
>     return &add; // the add function captured will remember the value of n
> }
[snip]
> Am i missing something?

You shouldn't return delegates to nested functions, just like you shouldn't return pointers to local variables.
Delegates to nested functions contain a pointer to the stack frame of the enclosing function. If that function has returned, the stack frame may be corrupted (especially if you have called another function, like apply(), since then).
January 11, 2007
Frits van Bommel Wrote:

> Alain wrote:
> > AddFunc addN(int n) {
> >     int add(int i) {
> >         return i + n;
> >     }
> >     return &add; // the add function captured will remember the value of n
> > }
> [snip]
> > Am i missing something?
> 
> You shouldn't return delegates to nested functions, just like you
> shouldn't return pointers to local variables.
> Delegates to nested functions contain a pointer to the stack frame of
> the enclosing function. If that function has returned, the stack frame
> may be corrupted (especially if you have called another function, like
> apply(), since then).

How come this compiles? Is it a bug ?

Alain
January 11, 2007
Alain wrote:
> Frits van Bommel Wrote:
> 
>> Alain wrote:
>>> AddFunc addN(int n) {
>>>     int add(int i) {
>>>         return i + n;
>>>     }
>>>     return &add; // the add function captured will remember the value of n
>>> }
>> [snip]
>>> Am i missing something?
>> You shouldn't return delegates to nested functions, just like you shouldn't return pointers to local variables.
>> Delegates to nested functions contain a pointer to the stack frame of the enclosing function. If that function has returned, the stack frame may be corrupted (especially if you have called another function, like apply(), since then).
> 
> How come this compiles? Is it a bug ?
> 
> Alain

This is valid code. It is up to the programmer to make sure the delegate  doesn't use vars that are out of scope. There was some talk about this, to copy the 'evironment' for later use, it might be solved in the future.

std.bind might be of use here.
January 11, 2007
Alain wrote:

> Hello,
> 
> I am experimenting with closures and i have problem with the following code:
> 
> import std.stdio;
> alias int delegate(int x) AddFunc;
> 
> AddFunc addN(int n) {
>     int add(int i) {
>         return i + n;
>     }
>     return &add; // the add function captured will remember the value of n
> }
> 
> void apply(int[] array, AddFunc func) {
>     foreach (inout int i; array) {
>         i = func(i);
>     }
> }
> 
> int main() {
>     int[] numbers = [1,2,3];
>     writefln("numbers before=",numbers);
>     apply(numbers, addN(40)); // add 40 to each number
>     writefln("numbers after=",numbers);
>     return 0;
> }
> 
> The result should be [41,42,43] but i get [4202694,4202695,4202696]
> 
> Am i missing something?
> 
> Alain

D doesn't support true closures, just delegates.  Local variables in functions are still allocated on the stack and therefore invalid after you return from them.

You must use objects for this purpose.

-DavidM
January 11, 2007
David Medlock Wrote:

> D doesn't support true closures, just delegates.  Local variables in functions are still allocated on the stack and therefore invalid after you return from them.
> 
> You must use objects for this purpose.
> 
> -DavidM
Thank you for the suggestion. I post hereunder my solution to the problem.

import std.stdio;

class ClassClosure
{
    this(int n)   { num = n;}
    private uint num;
    int opCall(int x)  {return num+x;}
}

void capply(int[] array, ClassClosure func) {
    foreach (inout int i; array) { i = func(i); }
    }

int main() {
    int[] numbers = [1,2,3];
    writefln("numbers before=",numbers);
    ClassClosure myclosure= new ClassClosure(40);
    capply(numbers, myclosure); // add 40 to each number
    writefln("numbers after=",numbers);
    return 0;
}

Alain
January 11, 2007
Here is an alternate solution. One which requires fewer changes to the original design. Only the contents of the addN function have been changed. It is not necessarily better than the other solution. Just different.

import std.stdio;
alias int delegate(int x) AddFunc;

AddFunc addN(int n) {
    class A {
        int n;
        this(int n) {
            this.n = n;
        }
        int add(int i) {
            return i + n;
        }
    }
    A a = new A(n);
    return &a.add; // the add function captured will remember the value of n
}

void apply(int[] array, AddFunc func) {
    foreach (inout int i; array) {
        i = func(i);
    }
}

int main() {
    int[] numbers = [1,2,3];
    writefln("numbers before=",numbers);
    apply(numbers, addN(40)); // add 40 to each number
    writefln("numbers after=",numbers);
    return 0;
}

Alain wrote:
> David Medlock Wrote:
> 
>> D doesn't support true closures, just delegates.  Local variables in functions are still allocated on the stack and therefore invalid after you return from them.
>>
>> You must use objects for this purpose.
>>
>> -DavidM
> Thank you for the suggestion. I post hereunder my solution to the problem.
> 
> import std.stdio;
> 
> class ClassClosure
> {
>     this(int n)   { num = n;}
>     private uint num;      int opCall(int x)  {return num+x;}
> }
> 
> void capply(int[] array, ClassClosure func) {
>     foreach (inout int i; array) { i = func(i); }
>     }
> 
> int main() {
>     int[] numbers = [1,2,3];
>     writefln("numbers before=",numbers);
>     ClassClosure myclosure= new ClassClosure(40);
>     capply(numbers, myclosure); // add 40 to each number
>     writefln("numbers after=",numbers);
>     return 0;
> }
> 
> Alain
January 11, 2007
There is also a templated Curry function example in the D template docs:

http://www.digitalmars.com/d/template.html

/* R is return type
 * A is first argument type
 * U is TypeTuple of rest of argument types
 */
R delegate(U) Curry(R, A, U...)(R delegate(A, U) dg, A arg)
{
    struct Foo
    {
	typeof(dg) dg_m;
	typeof(arg) arg_m;

	R bar(U u)
	{
	    return dg_m(arg_m, u);
	}
    }

    Foo* f = new Foo;
    f.dg_m = dg;
    f.arg_m = arg;
    return &f.bar;
}

void main()
{
    int plus(int x, int y, int z)
    {
	return x + y + z;
    }

    auto plus_two = Curry(&plus, 2);
    printf("%d\n", plus_two(6, 8));	// prints 16
}
January 12, 2007
Applying the Curry method to the current example yields the following.

import std.stdio;

alias int delegate(int x) AddFunc;

AddFunc addN(int n) {
    int add(int a, int b) {
        return a + b;
    }
    return Curry(&add, n); // the add function captured will remember the value of n
}

void apply(int[] array, AddFunc func) {
    foreach (inout int i; array) {
        i = func(i);
    }
}

int main() {
    int[] numbers = [1,2,3];
    writefln("numbers before=",numbers);
    apply(numbers, addN(40)); // add 40 to each number
    writefln("numbers after=",numbers);

    return 0;
}

/* R is return type
 * A is first argument type
 * U is TypeTuple of rest of argument types
 */
R delegate(U) Curry(R, A, U...)(R delegate(A, U) dg, A arg) {
    struct Closure {
        typeof(dg) originalDelegate;
        A value;

        R subDelegate(U u) {
            return originalDelegate(value, u);
        }
    }

    Closure* f = new Closure;
    f.originalDelegate = dg;
    f.value = arg;
    return &f.subDelegate;
}


Daniel Giddings wrote:
> There is also a templated Curry function example in the D template docs:
> 
> http://www.digitalmars.com/d/template.html
> 
> /* R is return type
>  * A is first argument type
>  * U is TypeTuple of rest of argument types
>  */
> R delegate(U) Curry(R, A, U...)(R delegate(A, U) dg, A arg)
> {
>     struct Foo
>     {
>     typeof(dg) dg_m;
>     typeof(arg) arg_m;
> 
>     R bar(U u)
>     {
>         return dg_m(arg_m, u);
>     }
>     }
> 
>     Foo* f = new Foo;
>     f.dg_m = dg;
>     f.arg_m = arg;
>     return &f.bar;
> }
> 
> void main()
> {
>     int plus(int x, int y, int z)
>     {
>     return x + y + z;
>     }
> 
>     auto plus_two = Curry(&plus, 2);
>     printf("%d\n", plus_two(6, 8));    // prints 16
> }
January 12, 2007
Or with a small modification to Curry to curry a function instead of a delegate:

import std.stdio;

alias int delegate(int x) AddFunc;

int add(int a, int b) {
	return a + b;
}

void apply(int[] array, AddFunc func) {
    foreach (inout int i; array) {
        i = func(i);
    }
}

int main() {
    int[] numbers = [1,2,3];
    writefln("numbers before=",numbers);
    apply(numbers, CurryFunc( &add, 40 ) ); // add 40 to each number
    writefln("numbers after=",numbers);

    return 0;
}

/* R is return type
 * A is first argument type
 * U is TypeTuple of rest of argument types
 */
R delegate(U) CurryFunc(R, A, U...)(R function(A, U) fn, A arg) {
    struct Closure {
        typeof(fn) originalFunction;
        typeof(A) value;

        R subDelegate(U u) {
            return originalFunction(value, u);
        }
    }

    Closure* f = new Closure;
    f.originalFunction = fn;
    f.value = arg;
    return &f.subDelegate;
}

Bradley Smith wrote:
> Applying the Curry method to the current example yields the following.
> 
> import std.stdio;
> 
> alias int delegate(int x) AddFunc;
> 
> AddFunc addN(int n) {
>     int add(int a, int b) {
>         return a + b;
>     }
>     return Curry(&add, n); // the add function captured will remember the value of n
> }
> 
> void apply(int[] array, AddFunc func) {
>     foreach (inout int i; array) {
>         i = func(i);
>     }
> }
> 
> int main() {
>     int[] numbers = [1,2,3];
>     writefln("numbers before=",numbers);
>     apply(numbers, addN(40)); // add 40 to each number
>     writefln("numbers after=",numbers);
> 
>     return 0;
> }
> 
> /* R is return type
>  * A is first argument type
>  * U is TypeTuple of rest of argument types
>  */
> R delegate(U) Curry(R, A, U...)(R delegate(A, U) dg, A arg) {
>     struct Closure {
>         typeof(dg) originalDelegate;
>         A value;
> 
>         R subDelegate(U u) {
>             return originalDelegate(value, u);
>         }
>     }
> 
>     Closure* f = new Closure;
>     f.originalDelegate = dg;
>     f.value = arg;
>     return &f.subDelegate;
> }
> 
> 
« First   ‹ Prev
1 2