lambda usages

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • puzzlecracker

    lambda usages

    I am not understanding what's happening in the code below (from
    accelerated c#) -- I know what the outcome but not how .net does it:

    using System;
    using System.Linq;


    public class LambdaTest
    {
    static void Main()
    {
    int counter = 0;
    WriteStream( () =counter++ );
    Console.WriteLi ne( "Final value of counter: {0}", counter );
    }
    static void WriteStream( Func<intcounter )
    {
    for( int i = 0; i < 10; ++i )
    {
    Console.Write( "{0}, ", counter() );
    }
    Console.WriteLi ne();
    }
    }

    From what I understand, we are creating Func generic delegate ,
    which is takes no parameters and returns the int. We and to that
    delegate we pass local value type counter. Then why would counter
    changed upon the retrun from WriteStream? Doesn't WriteSteam operate
    on the local counter argument?

    need some lambda education....

    Thanks
  • Mythran

    #2
    Re: lambda usages

    "puzzlecrac ker" <ironsel2000@gm ail.comwrote in message
    news:9b3e002d-0882-4a9c-8839-a7bc9bb1ce5e@p4 9g2000hsd.googl egroups.com...
    I am not understanding what's happening in the code below (from
    accelerated c#) -- I know what the outcome but not how .net does it:
    >
    using System;
    using System.Linq;
    >
    >
    public class LambdaTest
    {
    static void Main()
    {
    int counter = 0;
    WriteStream( () =counter++ );
    Console.WriteLi ne( "Final value of counter: {0}", counter );
    }
    static void WriteStream( Func<intcounter )
    {
    for( int i = 0; i < 10; ++i )
    {
    Console.Write( "{0}, ", counter() );
    }
    Console.WriteLi ne();
    }
    }
    >
    From what I understand, we are creating Func generic delegate ,
    which is takes no parameters and returns the int. We and to that
    delegate we pass local value type counter. Then why would counter
    changed upon the retrun from WriteStream? Doesn't WriteSteam operate
    on the local counter argument?
    >
    need some lambda education....
    >
    Thanks
    Well, we are not passing counter into the generic delegate, the generic
    delegate is using the local counter (local to the outer function) which is
    the same value at the same address when updated/modified from the Main
    function or the delegate. The "equivalent " method would be:

    static int Increment(ref int counter) { counter++; }

    By equivalent I mean that counter is being "passed" into the method (by
    reference) instead of the method accessing the variable of the parent code
    block directly.

    I believe this is correct but I'm not 100% so I hope someone else replies if
    I'm wrong :)

    HTH,
    Mythran


    Comment

    • Ben Voigt [C++ MVP]

      #3
      Re: lambda usages

      Mythran wrote:
      "puzzlecrac ker" <ironsel2000@gm ail.comwrote in message
      news:9b3e002d-0882-4a9c-8839-a7bc9bb1ce5e@p4 9g2000hsd.googl egroups.com...
      >I am not understanding what's happening in the code below (from
      >accelerated c#) -- I know what the outcome but not how .net does it:
      >>
      >using System;
      >using System.Linq;
      >>
      >>
      >public class LambdaTest
      >{
      > static void Main()
      > {
      > int counter = 0;
      > WriteStream( () =counter++ );
      > Console.WriteLi ne( "Final value of counter: {0}", counter );
      > }
      > static void WriteStream( Func<intcounter )
      > {
      > for( int i = 0; i < 10; ++i )
      > {
      > Console.Write( "{0}, ", counter() );
      > }
      > Console.WriteLi ne();
      > }
      >}
      >>
      >From what I understand, we are creating Func generic delegate ,
      >which is takes no parameters and returns the int. We and to that
      >delegate we pass local value type counter. Then why would counter
      >changed upon the retrun from WriteStream? Doesn't WriteSteam operate
      >on the local counter argument?
      >>
      >need some lambda education....
      >>
      >Thanks
      >
      Well, we are not passing counter into the generic delegate, the
      generic delegate is using the local counter (local to the outer
      function) which is the same value at the same address when
      updated/modified from the Main function or the delegate. The
      "equivalent " method would be:
      static int Increment(ref int counter) { counter++; }
      >
      By equivalent I mean that counter is being "passed" into the method
      (by reference) instead of the method accessing the variable of the
      parent code block directly.
      >
      I believe this is correct but I'm not 100% so I hope someone else
      replies if I'm wrong :)
      Mostly right, but the difference is that here we have a closure, the
      reference to the function's counter variable is part of the delegate. With
      a reference argument the caller (i.e. WriteStream) would have to pass the
      reference, here the caller isn't involved in any way.
      >
      HTH,
      Mythran

      Comment

      • puzzlecracker

        #4
        Re: lambda usages

        On Oct 22, 2:20 pm, "Ben Voigt [C++ MVP]" <r...@nospam.no spamwrote:
        Mythran wrote:
        "puzzlecrac ker" <ironsel2...@gm ail.comwrote in message
        news:9b3e002d-0882-4a9c-8839-a7bc9bb1ce5e@p4 9g2000hsd.googl egroups.com....
        I am not understanding what's happening in the code below (from
        accelerated c#) -- I know what the outcome but not how .net does it:
        >
        using System;
        using System.Linq;
        >
        public class LambdaTest
        {
         static void Main()
         {
            int counter = 0;
            WriteStream( () =counter++ );
            Console.WriteLi ne( "Final value of counter: {0}", counter );
         }
         static void WriteStream( Func<intcounter )
         {
             for( int i = 0; i < 10; ++i )
             {
                 Console.Write( "{0}, ", counter() );
             }
             Console.WriteLi ne();
          }
        }
        >
        From what  I understand, we are creating Func  generic delegate ,
        which is takes no parameters and returns the int. We and to that
        delegate we pass local value type counter. Then  why would counter
        changed upon the retrun from WriteStream? Doesn't WriteSteam operate
        on the local  counter argument?
        >
        need some lambda education....
        >
        Thanks
        >
        Well, we are not passing counter into the generic delegate, the
        generic delegate is using the local counter (local to the outer
        function) which is the same value at the same address when
        updated/modified from the Main function or the delegate.  The
        "equivalent " method would be:
        static int Increment(ref int counter) { counter++; }
        >
        By equivalent I mean that counter is being "passed" into the method
        (by reference) instead of the method accessing the variable of the
        parent code block directly.
        >
        I believe this is correct but I'm not 100% so I hope someone else
        replies if I'm wrong :)
        >
        Mostly right, but the difference is that here we have a closure, the
        reference to the function's counter variable is part of the delegate.  With
        a reference argument the caller (i.e. WriteStream) would have to pass the
        reference, here the caller isn't involved in any way.
        >
        >
        >
        HTH,
        Mythran
        Does anyone know what how Func is defined and implemented?

        Comment

        • Peter Duniho

          #5
          Re: lambda usages

          On Wed, 22 Oct 2008 09:56:33 -0700, puzzlecracker <ironsel2000@gm ail.com>
          wrote:
          [...] Then why would counter
          changed upon the retrun from WriteStream? Doesn't WriteSteam operate
          on the local counter argument?
          >
          need some lambda education....
          Mythran's reply mostly covers it. The main correction that needs making
          is that it's not that Main()'s "counter" local variable is passed by
          reference. It's "captured" by the lambda expression. That is, by using
          it in the lambda expression (which in this case is basically just a
          different way of declaring an anonymous method), the local variable winds
          up removed from the stack and being stored on the heap so that it can be
          used by the lambda expression even after the Main() method has returned.

          This is a significant point. If the variable were simply being passed by
          reference, then the lambda expression would be useless after Main() had
          returned (ignoring for the moment that Main() is a special method that,
          after it returns, terminates the process :) ). By capturing the variable,
          the lambda expression remains viable even after the Main() method has
          returned.

          This has a variety of implications, but one of them is that any time the
          lambda expression is executed, it's using the same "counter" variable that
          exists in the Main() method. If you modify the variable in the lambda
          expression, that's visible in the Main() method as well (and vice a versa).

          So, the WriteStream() method invokes the lambda expression ten times, and
          each time it's invoked, it increments the local variable in Main(). After
          the WriteStream() method returns, that local variable thus has been
          incremented ten times.

          Pete

          Comment

          • Peter Duniho

            #6
            Re: lambda usages

            On Wed, 22 Oct 2008 11:31:24 -0700, puzzlecracker <ironsel2000@gm ail.com>
            wrote:
            Does anyone know what how Func is defined and implemented?
            Func<Tis just a delegate type. You can declare it yourself if you like:

            delegate T Func<T>();

            It's not "implemente d" at all. But you can create an instance of the type
            in a variety of ways, all involving providing a method implementation that
            matches the signature of the delegate type. In the case of anonymous
            methods, the compiler can infer and cast to the correct type as long as
            the anonymous method is compatible (i.e you haven't written it in a way
            that is mutually exclusive with the desired type).

            Pete

            Comment

            • puzzlecracker

              #7
              Re: lambda usages

              This is a significant point. If the variable were simply being passed by
              reference, then the lambda expression would be useless after Main() had
              returned (ignoring for the moment that Main() is a special method that,
              after it returns, terminates the process :) ). By capturing the variable,
              the lambda expression remains viable even after the Main() method has
              returned.
              Is this really possible? I don't see how you can move life-time of
              the local counter variable beyond it's enclosed space, which is main
              our case. It's conceptually wrong to prolong a variables lifetime.

              Actually, on the second thought: perhaps ones example can be a
              returning the of lambada expression from a function which used its
              local variable to initialize it...


              Someone needs to demonstrate it with an example... any volunteers?

              Comment

              • puzzlecracker

                #8
                Re: lambda usages

                On Oct 22, 3:35 pm, "Peter Duniho" <NpOeStPe...@nn owslpianmk.com>
                wrote:
                On Wed, 22 Oct 2008 11:31:24 -0700, puzzlecracker <ironsel2...@gm ail.com>
                wrote:
                >
                Does anyone know what how Func is defined and implemented?
                >
                Func<Tis just a delegate type. You can declare it yourself if you like:
                >
                delegate T Func<T>();
                >
                It's not "implemente d" at all. But you can create an instance of the type
                in a variety of ways, all involving providing a method implementation that
                matches the signature of the delegate type. In the case of anonymous
                methods, the compiler can infer and cast to the correct type as long as
                the anonymous method is compatible (i.e you haven't written it in a way
                that is mutually exclusive with the desired type).
                >
                Pete
                I tend to think of delegates as classes, hence perceived Func as a
                functor...am a paltry and recovering C++ developer


                Comment

                • Peter Duniho

                  #9
                  Re: lambda usages

                  On Wed, 22 Oct 2008 19:03:48 -0700, puzzlecracker <ironsel2000@gm ail.com>
                  wrote:
                  Does anyone know what how Func is defined and implemented?
                  >>
                  >Func<Tis just a delegate type. You can declare it yourself if you
                  >like:
                  >>
                  > delegate T Func<T>();
                  >>
                  >It's not "implemente d" at all. [...]
                  >
                  I tend to think of delegates as classes, hence perceived Func as a
                  functor...am a paltry and recovering C++ developer
                  I'm not sure what you mean. A delegate type is a class and a delegate
                  instance can indeed be thought of as a functor.

                  There is in fact implementation behind a delegate type, but without a more
                  specific question it's not clear what you were asking by "defined and
                  implemented". I thought you were asking about the declared type Func<T>,
                  as opposed to the Delegate type that provides the implementation. I
                  suppose saying that Func<Tisn't "implemente d at all" is a bit
                  misleading; obviously there's an implementation somewhere. It's just that
                  there's nothing special about Func<Tas opposed to other delegate types.

                  Pete

                  Comment

                  • Peter Duniho

                    #10
                    Re: lambda usages

                    On Wed, 22 Oct 2008 19:01:01 -0700, puzzlecracker <ironsel2000@gm ail.com>
                    wrote:
                    >This is a significant point. If the variable were simply being passed
                    >by
                    >reference, then the lambda expression would be useless after Main() had
                    >returned (ignoring for the moment that Main() is a special method that,
                    >after it returns, terminates the process :) ). By capturing the
                    >variable,
                    >the lambda expression remains viable even after the Main() method has
                    >returned.
                    >
                    Is this really possible?
                    Yes, of course. I wouldn't have written it otherwise.
                    I don't see how you can move life-time of
                    the local counter variable beyond it's enclosed space, which is main
                    our case. It's conceptually wrong to prolong a variables lifetime.
                    It's definitely conceptually wrong. Except when it's right. :p
                    Actually, on the second thought: perhaps ones example can be a
                    returning the of lambada expression from a function which used its
                    local variable to initialize it...
                    >
                    Someone needs to demonstrate it with an example... any volunteers?
                    I thought the original example in this thread demonstrated it reasonably.
                    But taking your suggestion as a more overt version, here's what the code
                    would look like:

                    void MethodA()
                    {
                    Func<intfunc = MethodB();

                    Console.WriteLi ne(func());
                    Console.WriteLi ne(func());
                    }

                    Func<intMethodB ()
                    {
                    int i = 5;

                    return () =i++;
                    }

                    As you suggested, MethodB() returns the delegate. The variable i is
                    captured by the lambda expression and so the lifetime is moved out of the
                    local stack frame and into the heap so that the delegate created with the
                    lambda expression can always access it, even after MethodB() returns.

                    You'll find that if you call MethodA(), you get 5 and then 6 written to
                    standard output.

                    Pete

                    Comment

                    • Ben Voigt [C++ MVP]

                      #11
                      Re: lambda usages



                      "Peter Duniho" <NpOeStPeAdM@nn owslpianmk.comw rote in message
                      news:op.ujgh6ad v8jd0ej@petes-computer.local. ..
                      On Wed, 22 Oct 2008 19:03:48 -0700, puzzlecracker <ironsel2000@gm ail.com>
                      wrote:
                      >
                      >Does anyone know what how Func is defined and implemented?
                      >>>
                      >>Func<Tis just a delegate type. You can declare it yourself if you
                      >>like:
                      >>>
                      >> delegate T Func<T>();
                      >>>
                      >>It's not "implemente d" at all. [...]
                      >>
                      >I tend to think of delegates as classes, hence perceived Func as a
                      >functor...am a paltry and recovering C++ developer
                      >
                      I'm not sure what you mean. A delegate type is a class and a delegate
                      instance can indeed be thought of as a functor.
                      So we might say a delegate type is an interface for functors, or even (dare
                      I?), a "concept".

                      Comment

                      • Ben Voigt [C++ MVP]

                        #12
                        Re: lambda usages



                        "Peter Duniho" <NpOeStPeAdM@nn owslpianmk.comw rote in message
                        news:op.ujgiw10 o8jd0ej@petes-computer.local. ..
                        On Wed, 22 Oct 2008 19:01:01 -0700, puzzlecracker <ironsel2000@gm ail.com>
                        wrote:
                        >
                        >>This is a significant point. If the variable were simply being passed
                        >>by
                        >>reference, then the lambda expression would be useless after Main() had
                        >>returned (ignoring for the moment that Main() is a special method that,
                        >>after it returns, terminates the process :) ). By capturing the
                        >>variable,
                        >>the lambda expression remains viable even after the Main() method has
                        >>returned.
                        >>
                        >Is this really possible?
                        >
                        Yes, of course. I wouldn't have written it otherwise.
                        >
                        >I don't see how you can move life-time of
                        >the local counter variable beyond it's enclosed space, which is main
                        >our case. It's conceptually wrong to prolong a variables lifetime.
                        >
                        It's definitely conceptually wrong. Except when it's right. :p
                        >
                        >Actually, on the second thought: perhaps ones example can be a
                        >returning the of lambada expression from a function which used its
                        >local variable to initialize it...
                        >>
                        >Someone needs to demonstrate it with an example... any volunteers?
                        >
                        I thought the original example in this thread demonstrated it reasonably.
                        But taking your suggestion as a more overt version, here's what the code
                        would look like:
                        >
                        void MethodA()
                        {
                        Func<intfunc = MethodB();
                        >
                        Console.WriteLi ne(func());
                        Console.WriteLi ne(func());
                        }
                        >
                        Func<intMethodB ()
                        {
                        int i = 5;
                        >
                        return () =i++;
                        }
                        Use Reflector on this. You'll find that what looks like a local variable no
                        longer is. The captured local variables in any scope are moved into a new
                        anonymous class, which the anonymous delegate (written with lambda syntax
                        but that doesn't matter) is a member of so it gets the full context.
                        Something like:


                        Func<intMethodB ()
                        {
                        class Closure_Uniquif ied
                        {
                        int i;
                        int AnonDelegate01( ) { return i++; }
                        }
                        Closure_Uniquif ied captured = new Closure_Uniquif ied();
                        captured.i = 5;
                        return new Func<int>(captu red.AnonDelegat e01);
                        }
                        >
                        As you suggested, MethodB() returns the delegate. The variable i is
                        captured by the lambda expression and so the lifetime is moved out of the
                        local stack frame and into the heap so that the delegate created with the
                        lambda expression can always access it, even after MethodB() returns.
                        >
                        You'll find that if you call MethodA(), you get 5 and then 6 written to
                        standard output.
                        >
                        Pete

                        Comment

                        Working...