.. Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht Distributed under the terms of the BSD 3-Clause License. The full license is in the file LICENSE, distributed with this software. Expression tree =============== Most of the expressions in *xtensor* are lazy-evaluated, they do not hold any value, the values are computed upon access or when the expression is assigned to a container. This means that *xtensor* needs somehow to keep track of the expression tree. xfunction ~~~~~~~~~ A node in the expression tree may be represented by different classes in *xtensor*; here we focus on basic arithmetic operations and mathematical functions, which are represented by an instance of ``xfunction``. This is a template class whose parameters are: - a functor describing the operation of the mathematical function - the closures of the child expressions, i.e. the most optimal way to store each child expression Consider the following code: .. code:: xarray a = xt::ones({2, 2}); xarray b = xt::ones({2, 2}); auto f = (a + b); Here the type of ``f`` is ``xfunction&, const xarray&>``, and f stores constant references on the arrays involved in the operation. This can be illustrated by the figure below: .. image:: xfunction_tree.svg The implementation of ``xfunction`` methods is quite easy: they forward the call to the nodes and apply the operation when this makes sense. For instance, assuming that the operands are stored as ``m_first`` and ``m_second``, and the functor describing the operation as ``m_functor``, the implementation of ``operator()`` and ``broadcast_shape`` looks like: .. code:: template template inline auto xfunction::operator()(Args... args) const -> const_reference { return m_functor(m_first(args...), m_second(args...)); } template template inline bool xfunction::broadcast_shape(S& shape) const { return m_first.broadcast_shape(shape) && m_second.broadcast_shape(shape); } In fact, ``xfunction`` can handle an arbitrary number of arguments. The practical implementation is slightly more complicated than the code snippet above, however the principle remains the same. Holding expressions ~~~~~~~~~~~~~~~~~~~ Each node of an expression tree holds const references to its child nodes, or the child nodes themselves, depending on their nature. When building a complex expression, if a part of this expression is an rvalue, it is moved inside its parent, else a constant reference is used: .. code:: xarray some_function(); xarray a = xt::ones({2, 2}); auto f = a + some_function(); Here ``f`` holds a constant reference on ``a``, while the array returned by ``some_function`` is moved into ``f``. The actual types held by the expression are the **closure types**, more details can be found in :ref:`closure-semantics-label`. Building the expression tree ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As previously stated, each mathematical function in xtensor returns an instance of ``xfunction``. This section explains in details how the template parameters of ``xfunction`` are computed according to the type of the function, the number and the types of its arguments. Let's consider the definition of ``operator+``: .. code:: template inline auto operator+(E1&& e1, E2&& e2) -> detail::xfunction_type { return detail::make_xfunction(std::forward(e1), std::forward(e2)); } This top-level function selects the appropriate functor and forwards its arguments to the ``make_xfunction`` generator. This latter is responsible for setting the remaining template parameters of ``xfunction``: .. code:: template