[/============================================================================== Copyright (C) 2001-2010 Joel de Guzman Copyright (C) 2001-2005 Dan Marsden Copyright (C) 2001-2010 Thomas Heller Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ===============================================================================/] [section Scope] Up until now, the most basic ingredient is missing: creation of and access to local variables in the stack. When recursion comes into play, you will soon realize the need to have true local variables. It may seem that we do not need this at all since an unnamed lambda function cannot call itself anyway; at least not directly. With some sort of arrangement, situations will arise where a lambda function becomes recursive. A typical situation occurs when we store a lambda function in a [@http://www.boost.org/libs/function Boost.Function], essentially naming the unnamed lambda. There will also be situations where a lambda function gets passed as an argument to another function. This is a more common situation. In this case, the lambda function assumes a new scope; new arguments and possibly new local variables. This section deals with local variables and nested lambda scopes. [section Local Variables] #include We use an instance of: expression::local_variable::type to represent a local variable. The local variable acts as an imaginary data-bin where a local, stack based data will be placed. `Key` is an arbitrary type that is used to identify the local variable. Example: struct size_key; expression::local_variable::type size; [*Predefined Local Variables] There are a few predefined instances of `expression::local_variable::type` named `_a`..`_z` that you can already use. To make use of them, simply use the `namespace boost::phoenix::local_names`: using namespace boost::phoenix::local_names; [endsect] [section let] #include You declare local variables using the syntax: let(local-declarations) [ let-body ] `let` allows 1..N local variable declarations (where N == `BOOST_PHOENIX_LOCAL_LIMIT`). Each declaration follows the form: local-id = lambda-expression [note You can set `BOOST_PHOENIX_LOCAL_LIMIT`, the predefined maximum local variable declarations in a let expression. By default, `BOOST_PHOENIX_LOCAL_LIMIT` is set to `BOOST_PHOENIX_LIMIT`.] Example: let(_a = 123, _b = 456) [ _a + _b ] [*Reference Preservation] The type of the local variable assumes the type of the lambda- expression. Type deduction is reference preserving. For example: let(_a = arg1, _b = 456) `_a` assumes the type of `arg1`: a reference to an argument, while `_b` has type `int`. Consider this: int i = 1; let(_a = arg1) [ cout << --_a << ' ' ] (i); cout << i << endl; the output of above is : 0 0 While with this: int i = 1; let(_a = val(arg1)) [ cout << --_a << ' ' ] (i); cout << i << endl; the output is : 0 1 Reference preservation is necessary because we need to have L-value access to outer lambda-scopes (especially the arguments). `arg`s and `ref`s are L-values. `val`s are R-values. [*Visibility] [#phoenix.modules.scope.let.visibility] The scope and lifetimes of the local variables is limited within the let-body. `let` blocks can be nested. A local variable may hide an outer local variable. For example: let(_x = _1, _y = _2) [ // _x here is an int: 1 let(_x = _3) // hides the outer _x [ cout << _x << _y // prints "Hello, World" ] ](1," World","Hello,"); The actual values of the parameters _1, _2 and _3 are supplied from the bracketed list at the end of the `let`. There is currently a limitation that the inner `let` cannot be supplied with a constant e.g. `let(_x = 1)`. The RHS (right hand side lambda-expression) of each local-declaration cannot refer to any LHS local-id. At this point, the local-ids are not in scope yet; they will only be in scope in the let-body. The code below is in error: let( _a = 1 , _b = _a // Error: _a is not in scope yet ) [ // _a and _b's scope starts here /*. body .*/ ] However, if an outer let scope is available, this will be searched. Since the scope of the RHS of a local-declaration is the outer scope enclosing the let, the RHS of a local-declaration can refer to a local variable of an outer scope: let(_a = 1) [ let( _a = _1 , _b = _a // Ok. _a refers to the outer _a ) [ /*. body .*/ ] ](1) [endsect] [section lambda] #include A lot of times, you'd want to write a lazy function that accepts one or more functions (higher order functions). STL algorithms come to mind, for example. Consider a lazy version of `stl::for_each`: struct for_each_impl { template struct result { typedef void type; }; template void operator()(C& c, F f) const { std::for_each(c.begin(), c.end(), f); } }; function const for_each = for_each_impl(); Notice that the function accepts another function, `f` as an argument. The scope of this function, `f`, is limited within the `operator()`. When `f` is called inside `std::for_each`, it exists in a new scope, along with new arguments and, possibly, local variables. This new scope is not at all related to the outer scopes beyond the `operator()`. Simple syntax: lambda [ lambda-body ] Like `let`, local variables may be declared, allowing 1..N local variable declarations (where N == `BOOST_PHOENIX_LOCAL_LIMIT`): lambda(local-declarations) [ lambda-body ] The same restrictions apply with regard to scope and visibility. The RHS (right hand side lambda-expression) of each local-declaration cannot refer to any LHS local-id. The local-ids are not in scope yet; they will be in scope only in the lambda-body: lambda( _a = 1 , _b = _a // Error: _a is not in scope yet ) See [link phoenix.modules.scope.let.visibility `let` Visibility] for more information. Example: Using our lazy `for_each` let's print all the elements in a container: for_each(arg1, lambda[cout << arg1]) As far as the arguments are concerned (arg1..argN), the scope in which the lambda-body exists is totally new. The left `arg1` refers to the argument passed to `for_each` (a container). The right `arg1` refers to the argument passed by `std::for_each` when we finally get to call `operator()` in our `for_each_impl` above (a container element). Yet, we may wish to get information from outer scopes. While we do not have access to arguments in outer scopes, what we still have is access to local variables from outer scopes. We may only be able to pass argument related information from outer `lambda` scopes through the local variables. [note This is a crucial difference between `let` and `lambda`: `let` does not introduce new arguments; `lambda` does.] Another example: Using our lazy `for_each`, and a lazy `push_back`: struct push_back_impl { template struct result { typedef void type; }; template void operator()(C& c, T& x) const { c.push_back(x); } }; function const push_back = push_back_impl(); write a lambda expression that accepts: # a 2-dimensional container (e.g. `vector >`) # a container element (e.g. `int`) and pushes-back the element to each of the `vector`. Solution: for_each(arg1, lambda(_a = arg2) [ push_back(arg1, _a) ] ) Since we do not have access to the arguments of the outer scopes beyond the lambda-body, we introduce a local variable `_a` that captures the second outer argument: `arg2`. Hence: _a = arg2. This local variable is visible inside the lambda scope. (See [@../../example/lambda.cpp lambda.cpp]) [endsect] [endsect]