This is an automated email from the ASF dual-hosted git repository. ddekany pushed a commit to branch 2.3-gae in repository https://gitbox.apache.org/repos/asf/freemarker.git
The following commit(s) were added to refs/heads/2.3-gae by this push: new 070d869 Added more information to the Manual, related to local lambdas and related built-ins. Some clarifications. 070d869 is described below commit 070d8694d5df241abe5504f6dd8c24a5ca792f43 Author: ddekany <ddek...@apache.org> AuthorDate: Sun Aug 4 19:08:26 2019 +0200 Added more information to the Manual, related to local lambdas and related built-ins. Some clarifications. --- src/manual/en_US/book.xml | 222 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 189 insertions(+), 33 deletions(-) diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml index bafa8af..fda828d 100644 --- a/src/manual/en_US/book.xml +++ b/src/manual/en_US/book.xml @@ -848,6 +848,14 @@ All Rights Reserved. true of false depending on if <literal>user</literal> starts with the letter <quote>J</quote> or not.</para> </listitem> + + <listitem> + <para><literal>animals?filter(it -> it.protected)</literal> + gives the list of protected animals. To list protected animals + only, you could use <literal><#list animals?filter(it -> + it.protected) as + animal><replaceable>...</replaceable></#list></literal>.</para> + </listitem> </itemizedlist> <para>Built-in applications can be chained, like @@ -3828,7 +3836,7 @@ Jerry</programlisting> <warning> <para>If you have a composite expression after the <literal>!</literal>, like <literal>1 + x</literal>, - <emphasis>always</emphasis> use parenthesses, like + <emphasis>always</emphasis> use parentheses, like <literal>${x!(1 + y)}</literal> or <literal>${(x!1) + y)}</literal>, depending on which interpretation you meant. That's needed because due to a programming mistake in FreeMarker @@ -4034,6 +4042,26 @@ Creating mouse... before the enclosing variable scope is ended. Hence, and to differentiate them from <quote>real</quote> lambdas, these are called <emphasis>local</emphasis> lambdas.</para> + + <para>The syntax of lambdas is like + <literal>(<replaceable>name1</replaceable>, + <replaceable>name2</replaceable>, <replaceable>...</replaceable>, + <replaceable>nameN</replaceable>) -> + <replaceable>expression</replaceable></literal>. If there's only a + single argument, the parentheses can be omitted: + <literal><replaceable>name1</replaceable> -> + <replaceable>expression</replaceable></literal>.</para> + + <para>As the right side of the <literal>-></literal> is just a + single expression, if you need complex logic there, you probably + want to move that into a <link + linkend="ref.directive.function">function</link>, as the you can use + directives like <literal>if</literal>, <literal>list</literal>, etc. + In that case though, you don't need a lambda expression, as all + built-ins that support a lambda parameter, also support passing in a + function directly. For example, instead of <literal>seq?map(x -> + myMapper(x))</literal> you should just write + <literal>seq?map(myMapper)</literal>.</para> </section> <section xml:id="dgui_template_exp_parentheses"> @@ -17033,9 +17061,11 @@ N <secondary>drop while</secondary> </indexterm> - <para>Filters out elements that match the parameter condition - (predicate), and stops filtering out elements with the first element - that does not match the condition. See the <link + <para>Returns a new sequence that contains the elements fro the + input sequence starting with from the first element that does + <emphasis>not</emphasis> match the parameter predicate (condition). + After that, all elements are included, regardless if they match the + predicate or not. See the <link linkend="ref_builtin_filter"><literal>filter</literal> built-in</link> for more details about parameters, and other details, but note that the condition in <literal>filter</literal> @@ -17062,6 +17092,10 @@ Filer for positives: match the predicate (<literal>x > 0</literal>). On the other hand, <literal>filter</literal> keeps the elements that match the same predicate, and it doesn't stop.</para> + + <para>See also: <link + linkend="ref_builtin_take_while"><literal>take_while</literal> + built-in</link></para> </section> <section xml:id="ref_builtin_filter"> @@ -17081,8 +17115,9 @@ Filer for positives: <para>This built-in is available since 2.3.29</para> </note> - <para>This built-in only keeps elements from a sequence that match - the parameter condition (predicate). For example:</para> + <para>Returns a new sequence that only contains the elements for + which the parameter condition (the predicate) returns + <literal>true</literal>. For example:</para> <programlisting role="template"><#assign xs = [1, -2, 3, 4, -5]> Positives: @@ -17095,9 +17130,9 @@ Negatives: Negatives: -2 -5 </programlisting> - <para>This built-in has a single required parameter, which specifies - the filter condition (what to keep). The filter condition can be - specified in 3 ways:</para> + <para>This built-in has a single required parameter, the predicate + (filter condition, what to keep). The predicate can be specified in + 3 ways:</para> <itemizedlist> <listitem> @@ -17120,10 +17155,11 @@ Negatives: </listitem> <listitem> - <para>A <link linkend="ref_directive_function">function</link> - or method that has a single argument, and returns boolean. For - example, the <quote>Negatives</quote> example above could be - implemented like this:</para> + <para>As a <link + linkend="ref_directive_function">function</link> or method that + has a single argument, and returns boolean. For example, the + <quote>Negatives</quote> example above could be implemented like + this:</para> <programlisting role="template"><#function negative(x)> <#return x < 0> @@ -17541,11 +17577,29 @@ red, green, blue. <primary>map built-in</primary> </indexterm> - <para>[TODO]</para> + <indexterm> + <primary>sequence</primary> + + <secondary>filter</secondary> + </indexterm> + + <para>Returns an new sequence where all elements are replaced with + the result of the parameter lambda, function, or method. For + example, you have a list of user objects in + <literal>users</literal>, but instead you need a list of user names + in variable, then you could do this:</para> + + <programlisting role="template"><#assign userNames = users?map(user -> user.name)></programlisting> + + <para>The parameter work like the parameter of the with <link + linkend="ref_builtin_filter"><literal>filter</literal> + built-in</link> (so see there), except that the + lambda/function/method you specify can return values of any + type.</para> <para>Regarding lazy evaluation, and handling of very long inputs, - it <link linkend="topic.filterLazyEval">works on the same way</link> - as with the <link + it also <link linkend="topic.filterLazyEval">works on the same + way</link> as the <link linkend="ref_builtin_filter"><literal>filter</literal> built-in</link>.</para> </section> @@ -17908,12 +17962,11 @@ Sorted by name.last: <secondary>take while</secondary> </indexterm> - <para>This is very similar to the <link - linkend="ref_builtin_filter"><literal>filter</literal> - built-in</link>, but it filters out all element starting with first - element that doesn't match the argument condition (predicate). See + <para>Returns a sequence that only contains the elements of the + input sequence which are before the first element that doesn't match + the parameter predicate (filter condition). This is very similar to the <link linkend="ref_builtin_filter"><literal>filter</literal> - built-in</link> for more details.</para> + built-in</link>, so see further details there.</para> <para>Example and comparison with <literal>filter</literal>:</para> @@ -17935,6 +17988,10 @@ Filer for positives: the first number that didn't match the predicate (<literal>x > 0</literal>), while <literal>filter</literal> has continued finding further matches.</para> + + <para>See also: <link + linkend="ref_builtin_drop_while"><literal>drop_while</literal> + built-in</link></para> </section> </section> @@ -21611,9 +21668,9 @@ All rights reserved.</emphasis></programlisting> <replaceable>Part repeated for each key-value pair</replaceable> </#list></literal></programlisting> - <para>But these are just cases of the generic forms, which are shown - below. Note that for simplicity we only show the generic forms for - sequence listing; simply replace <quote><literal>as + <para>But these are just special cases of the generic forms, which + are shown below. Note that for simplicity we only show the generic + forms for sequence listing; simply replace <quote><literal>as <replaceable>item</replaceable></literal></quote> with <quote><literal>as <replaceable>key</replaceable>, <replaceable>value</replaceable></literal></quote> to get the @@ -21926,6 +21983,16 @@ All rights reserved.</emphasis></programlisting> <primary>break directive</primary> </indexterm> + <note> + <para><literal>break</literal> is deprecated for most use cases, + as it doesn't work well with <literal><#sep></literal> and + <literal><replaceable>item</replaceable>?has_next</literal>. + Instead, use <link + linkend="ref_builtin_take_while"><literal><replaceable>sequence</replaceable>?take_while(<replaceable>predicate</replaceable>)</literal></link> + to cut the sequence before you list it. See also examples <link + linkend="ref_list_skipping">here.</link></para> + </note> + <para>You can exit the iteration at any point with the <literal>break</literal> directive. For example:</para> @@ -21966,10 +22033,11 @@ All rights reserved.</emphasis></programlisting> <literal>break</literal>-able directive.</para> <para>Using <literal>break</literal> together with - <literal>sep</literal> is generally a bad idea, as - <literal>sep</literal> can't know if you will skip the rest of - items with <literal>break</literal>, and then you end up with a - separator after the item printed last.</para> + <literal>sep</literal> or <literal>?has_next</literal> is + generally a bad idea, as these can't know if you will skip the + rest of items with a <literal>break</literal>. To solve such + situations see <link linkend="ref_list_skipping">these + examples</link>.</para> <para>Just like <literal>else</literal> and <literal>items</literal>, <literal>break</literal> must be @@ -21987,8 +22055,22 @@ All rights reserved.</emphasis></programlisting> </indexterm> <note> + <para><literal>continue</literal> is deprecated for most use + cases, as it doesn't work well with + <literal><#sep></literal>, + <literal><replaceable>item</replaceable>?has_next</literal>, + <literal><replaceable>item</replaceable>?count</literal>, + <literal><replaceable>item</replaceable>?index</literal>, + <literal><replaceable>item</replaceable>?item_parity</literal>, + etc. Instead, use <link + linkend="ref_builtin_filter"><literal><replaceable>sequence</replaceable>?filter(<replaceable>predicate</replaceable>)</literal></link> + to remove unwanted elements. See also examples <link + linkend="ref_list_skipping">here.</link></para> + </note> + + <note> <para>The <literal>continue</literal> directive exists since - FreeMarker 2.3.27</para> + FreeMarker 2.3.27.</para> </note> <para>You can skip the rest of the iteration body (the section @@ -22026,10 +22108,12 @@ All rights reserved.</emphasis></programlisting> <para>When you call <literal>continue</literal>, the <literal>sep</literal> directive will not be executed for that iteration. Using <literal>continue</literal> together with - <literal>sep</literal> is generally a bad idea, as - <literal>sep</literal> can't know if you will skip the rest of the - items, and then you end up with a separator after the item printed - last.</para> + <literal>sep</literal> is generally a bad idea anyway, also + <literal>?has_next</literal>, <literal>?count</literal>, + <literal>?index</literal>, <literal>?item_parity</literal>, etc. + will not work as you certainly wanted if you completely skip + items. To solve such situations see <link + linkend="ref_list_skipping">these examples</link>.</para> <para>Just like <literal>break</literal>, <literal>continue</literal> must be literally inside body of the @@ -22104,6 +22188,78 @@ All rights reserved.</emphasis></programlisting> 1}</literal>.</para> </section> + <section xml:id="ref_list_skipping"> + <title>Skipping items conditionally</title> + + <para>If you need to skip certain element in a list, it's + generally a bad idea to use <link + linkend="ref.directive.if"><literal>if</literal> directive</link> + for that, because then <literal><#sep></literal>, + <literal><replaceable>item</replaceable>?has_next</literal>, + <literal><replaceable>item</replaceable>?count</literal>, + <literal><replaceable>item</replaceable>?index</literal>, + <literal><replaceable>item</replaceable>?item_parity</literal>, + etc., will not be usable, as FreeMarker doesn't know what items + were and will be actually displayed. Instead, you should try to + remove the unwanted items from the sequence that you will list, + and then list it (since 2.3.29). Here are some typical examples + with and without <literal>if</literal>.</para> + + <simplesect> + <title>Filtering</title> + + <para>In this example, you want to show the recommended products + from <literal>products</literal>. Here's the wrong solution with + <literal>if</literal>:</para> + + <programlisting role="template"><#-- WRONG solution! The row parity classes will be possibly messed up: --> +<#list products as product> + <#<emphasis>if product.recommended</emphasis>> + <div class="${product<emphasis>?item_parity</emphasis>}Row">${product.name}</div> + </#if> +</#list></programlisting> + + <para>Here's the good solution that uses the <link + linkend="ref_builtin_filter"><literal>filter</literal> + built-in</link>:</para> + + <programlisting role="template"><#-- Good solution: --> +<#list products<emphasis>?filter(p -> p.recommended)</emphasis> as product> + <div class="${product?item_parity}Row">${product.name}</div> +</#list></programlisting> + </simplesect> + + <simplesect> + <title>Stop listing when a certain element is found</title> + + <para>Let's say you have a list of lines in + <literal>lines</literal>, and you need to stop at the first + empty line (if there's any). Furthermore you need to + <literal><br></literal> between the elements. Here's the + wrong solution with <literal>if</literal> and + <literal>break</literal>:</para> + + <programlisting role="template"><#-- WRONG solution! <br> might be added after the last printed line: --> +<#list lines as line> + <#if line == ''> + <#break> + </#if> + ${line}<#sep><br> +</#list></programlisting> + + <para>Here's the good solution that uses the <link + linkend="ref_builtin_take_while"><literal>take_while</literal> + built-in</link> (note that the condition is inverted compared to + the <literal>if</literal>+<literal>break</literal> + solution):</para> + + <programlisting role="template"><#-- Good solution: --> +<#list lines?take_while(line -> line != '') as line> + ${line}<#sep><br> +</#list></programlisting> + </simplesect> + </section> + <section xml:id="ref_list_nesting"> <title>Nesting loops into each other</title>