зеркало из https://github.com/github/codeql.git
docs: expand handbook entry on monotonic aggregates
This commit is contained in:
Родитель
e1644dd68b
Коммит
b32683fc9f
|
@ -393,16 +393,73 @@ aggregation in a simpler form:
|
||||||
Monotonic aggregates
|
Monotonic aggregates
|
||||||
====================
|
====================
|
||||||
|
|
||||||
In addition to the standard aggregates, QL also supports monotonic aggregates.
|
In addition to standard aggregates, QL also supports monotonic aggregates.
|
||||||
These are a slightly different way of computing aggregates which have some advantages.
|
Monotonic aggregates differ from standard aggregates in the way that they deal with the
|
||||||
For example, you can use monotonic aggregates :ref:`recursively <recursion>`.
|
values generated by the ``<expression>`` part of the formula:
|
||||||
You can't do this with normal aggregates.
|
|
||||||
|
|
||||||
For more information and examples, see `Monotonic aggregates in QL
|
- Standard aggregates take the ``<expression>`` values for each ``<formula>`` value and
|
||||||
<https://help.semmle.com/QL/learn-ql/advanced/monotonic-aggregates.html>`_.
|
flatten them into a list. A single aggregation function is applied to all the values.
|
||||||
|
- Monotonic aggregates take an ``<expression>`` for each value given by the ``<formula>``,
|
||||||
|
and create combinations of all the possible values. The aggregation
|
||||||
|
function is applied to each of the resulting combinations.
|
||||||
|
|
||||||
.. TODO: Eventually replace this link with just the relevant examples.
|
In general, if the ``<expression>`` is total and functional, then monotonic aggregates are
|
||||||
(Some of the content is a duplicate of the above discussion on aggregates.)
|
equivalent to standard aggregates. Results differ when there is not precisely one ``<expression>``
|
||||||
|
value for each value generated by the ``<formula>``:
|
||||||
|
|
||||||
|
- If there are missing ``<expression>`` values (that is, there is less than one
|
||||||
|
``<expression>`` value for each value generated by the ``<formula>``), monotonic aggregates
|
||||||
|
won't compute a result, as you cannot create combinations of values
|
||||||
|
including exactly one ``<expression>`` value for each value generated by the ``<formula>``.
|
||||||
|
|
||||||
|
- If there is more than one ``<expression>`` per ``<formula>`` result, you can create multiple
|
||||||
|
combinations of values including exactly one ``<expression>`` value for each
|
||||||
|
value generated by the ``<formula>``. Here, the aggregation function is applied to each of the
|
||||||
|
resulting combinations.
|
||||||
|
|
||||||
|
Recursive monotonic aggregates
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Monotonic aggregates may be used :ref:`recursively <recursion>`, but the recursive call may only appear in the
|
||||||
|
expression, and not in the range. The recursive semantics for aggregates are the same as the
|
||||||
|
recursive semantics for the rest of QL. For example, we might define a predicate to calculate
|
||||||
|
the distance of a node in a graph from the leaves as follows:
|
||||||
|
|
||||||
|
.. code-block:: ql
|
||||||
|
|
||||||
|
int depth(Node n) {
|
||||||
|
if not exists(n.getAChild())
|
||||||
|
then result = 0
|
||||||
|
else result = 1 + max(Node child | child = n.getAChild() | depth(child))
|
||||||
|
}
|
||||||
|
|
||||||
|
Here the recursive call is in the expression, which is legal. The recursive semantics for aggregates
|
||||||
|
are the same as the recursive semantics for the rest of QL. If you understand how aggregates work in
|
||||||
|
the non-recursive case then you should not find it difficult to use them recursively. However, it is
|
||||||
|
worth seeing how the evaluation of a recursive aggregation proceeds.
|
||||||
|
|
||||||
|
Consider the depth example we just saw with the following graph as input (arrows point from children to parents):
|
||||||
|
|
||||||
|
.. |image0| image:: ../images/monotonic-aggregates-graph.png
|
||||||
|
|
||||||
|
|image0|
|
||||||
|
|
||||||
|
Then the evaluation of the ``depth`` predicate proceeds as follows:
|
||||||
|
|
||||||
|
+-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| **Stage** | **depth** | **Comments** |
|
||||||
|
+===========+============================================+==========================================================================================================================================================================+
|
||||||
|
| 0 | | We always begin with the empty set. |
|
||||||
|
+-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| 1 | ``(0, b), (0, d), (0, e)`` | The nodes with no children have depth 0. The recursive step for **a** and **c** fails to produce a value, since some of their children do not have values for ``depth``. |
|
||||||
|
+-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| 2 | ``(0, b), (0, d), (0, e), (1, c)`` | The recursive step for **c** succeeds, since ``depth`` now has a value for all its children (**d** and **e**). The recursive step for **a** still fails. |
|
||||||
|
+-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| 3 | ``(0, b), (0, d), (0, e), (1, c), (2, a)`` | The recursive step for **a** succeeds, since ``depth`` now has a value for all its children (**b** and **c**). |
|
||||||
|
+-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
Here, we can see that at the intermediate stages it is very important for the aggregate to
|
||||||
|
fail if some of the children lack a value - this prevents erroneous values being added.
|
||||||
|
|
||||||
.. index:: any
|
.. index:: any
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче