You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2019/08/04 17:08:40 UTC
[freemarker] branch 2.3-gae updated: Added more information to the
Manual, related to local lambdas and related built-ins. Some clarifications.
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 <dd...@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>