123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- [/==============================================================================
- 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 <boost/phoenix/scope/local_variable.hpp>
- We use an instance of:
- expression::local_variable<Key>::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<size_key>::type size;
- [*Predefined Local Variables]
- There are a few predefined instances of `expression::local_variable<Key>::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 <boost/phoenix/scope/let.hpp>
- 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 <boost/phoenix/scope/lambda.hpp>
- 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 <typename C, typename F>
- struct result
- {
- typedef void type;
- };
- template <typename C, typename F>
- void operator()(C& c, F f) const
- {
- std::for_each(c.begin(), c.end(), f);
- }
- };
- function<for_each_impl> 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 <typename C, typename T>
- struct result
- {
- typedef void type;
- };
- template <typename C, typename T>
- void operator()(C& c, T& x) const
- {
- c.push_back(x);
- }
- };
- function<push_back_impl> const push_back = push_back_impl();
- write a lambda expression that accepts:
- # a 2-dimensional container (e.g. `vector<vector<int> >`)
- # a container element (e.g. `int`)
- and pushes-back the element to each of the `vector<int>`.
- 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]
|