LATEX-L Archives

Mailing list for the LaTeX3 project

LATEX-L@LISTSERV.UNI-HEIDELBERG.DE

Options: Use Forum View

Use Proportional Font
Show Text Part by Default
Show All Mail Headers

Message: [<< First] [< Prev] [Next >] [Last >>]
Topic: [<< First] [< Prev] [Next >] [Last >>]
Author: [<< First] [< Prev] [Next >] [Last >>]

Print Reply
Subject:
From:
Morten Høgholm <[log in to unmask]>
Reply To:
Mailing list for the LaTeX3 project <[log in to unmask]>
Date:
Thu, 23 Sep 2010 01:08:37 +0200
Content-Type:
text/plain
Parts/Attachments:
text/plain (189 lines)
On Tue, Sep 21, 2010 at 1:45 PM, Joseph Wright
<[log in to unmask]> wrote:

Late in the game...

> On 21/09/2010 06:32, Arno Trautmann wrote:
>>>
>>> Why not just use&&  in the first place?
>>
>> Because of possible catcode-troubles? ;)
>> Or maybe such a list might be more useful for a xor operation.
>
> How does the following look? The Boolean expressions are the most basic
> versions (NOT takes one argument, others take two), but nesting seems to
> work fine. There is definitely less code to this than the current
> implementation, so there would be a performance gain.

I know one could do this the way you have outlined. There are,
however, a few gotcha (as always in TeX), which is why there have been
different implementations. If you look back in time in the svn, you
will also find that there was actually both "ands" and "ors" functions
at one point.

One typical gotcha when inside the \if machinery is ensuring we do not
by accident put in things that will confuse the scanner. Thus
modifying the example to say \fi: instead of \ERROR will now make it
fail. This can of course be easily alleviated by carefully issuing
\use_i:nn etc. in appropriate places so that the dangerous parts are
not skipped by TeX's primitive scanner.

If we are talking about user interface then I'd like to say that I am
no big fan of having a plethora of \bool_and_p:nn, \bool_and_p:nnn,
etc. Just too many functions for my taste. Instead, I'd like something
simple such as
  \bool_and_p:n{ expr 1, expr 2, ..., expr n}
  \bool_or_p:n{ expr 1, expr 2, ..., expr n}

This can be done in a simple and efficient way that does not involve
primitive TeX conditionals. All it does is assume other _p functions
return \c_true_bool or \c_false_bool *after* finishing any TeX
conditional (as documented for a long time now). Idea is to evaluate
the first predicate in the list into a 0 or 1 and then use a case
switch depending on the result.



\tl_new:Nn \c_bool_stop_tl{2~}% magic stop marker.

\cs_set:Nn \bool_and_p:n { \exp_after:wN \bool_and_p_aux:Nn
\tex_number:D #1 ,\c_bool_stop_tl, }
\cs_set:Nn \bool_and_p_aux:Nn {
  \tex_ifcase:D #1~% number already expanded so a space to terminate
% case 0: a false, so we return false.
    \exp_after:wN \bool_end_list_false:w
  \or: % case 1: true, try next one.
    \exp_after:wN  \bool_and_p_aux:Nn \tex_number:D % yes, this works!
% because \tex_number:D resolves the ending \fi: for us.
  \or: % case 2: made it to the end, hence true
    \exp_after:wN \c_true_bool
  \fi:
}
\cs_set:Nn \bool_or_p:n { \exp_after:wN \bool_or_p_aux:Nn
\tex_number:D #1 ,\c_bool_stop_tl, }
\cs_set:Nn \bool_or_p_aux:Nn {
  \tex_ifcase:D #1~% false, try next
    \exp_after:wN  \bool_or_p_aux:Nn \tex_number:D
  \or: % true, skip to end
    \exp_after:wN \bool_end_list_true:w
  \or: % no true found, hence false
    \exp_after:wN \c_false_bool
  \fi:
}
% skipping the remainder when result is known.
\cs_set:Npn \bool_end_list_false:w #1\c_bool_stop_tl#2{\c_false_bool}
\cs_set:Npn \bool_end_list_true:w #1\c_bool_stop_tl #2{\c_true_bool}


Again, minimal evaluation is done. Also, one can in reality use
arbitrary single token delimiters, so
  \bool_or_p:n{\c_false_bool,\c_true_bool, \c_true_bool}
and
  \bool_or_p:n{\c_false_bool ; \c_true_bool ; \c_true_bool}
would both be legal.

And we can of course easily define \bool_and_p:nnnn to be just
  \bool_and_p:n {#1,#2,#3,#4}
if one wants to.


A few comments below.

> \documentclass{article}
> \usepackage{expl3}
> \ExplSyntaxOn
> \cs_set_nopar:Npn \bool_if:nT #1 {
>  \tex_ifodd:D \bool_if_p:n {#1}
>    \tex_expandafter:D \use:n
>  \tex_else:D
>    \tex_expandafter:D \use_none:n
>  \tex_fi:D
> }
> \cs_set_nopar:Npn \bool_if:nF #1 {
>  \tex_ifodd:D \bool_if_p:n {#1}
>    \tex_expandafter:D \use_none:n
>  \tex_else:D
>    \tex_expandafter:D \use:n
>  \tex_fi:D
> }
> \cs_set_nopar:Npn \bool_if:nTF #1 {
>  \tex_ifodd:D \bool_if_p:n {#1}
>    \tex_expandafter:D \use_i:nn
>  \tex_else:D
>    \tex_expandafter:D \use_ii:nn
>  \tex_fi:D
> }
> \cs_set_nopar:Npn \bool_if_p:n #1 {
>  \tex_number:D #1 ~
> }

I do not quite understand why this is done. Also, it does not return
the correct token but a digit instead so not a proper predicate
function.

> \cs_set_nopar:Npn \bool_not_p:n #1 {
>  \tex_ifnum:D \tex_number:D #1 = \c_true_bool

No need for any equality test or \tex_number:D . Just use
  \if_predicate:w #1 \exp_after:wN \c_false_bool \tex_else:D
\exp_after:wN \c_true_bool \tex_fi:D

Generally just use \if_predicate:w instead of the \tex_ifnum:D. Less
input and clearer what is going on.

>    \c_false_bool
>  \tex_else:D
>    \c_true_bool
>  \tex_fi:D
> }
> \cs_set_nopar:Npn \bool_and_p:nn #1#2 {
>  \tex_ifnum:D \tex_number:D #1 = \c_true_bool
>    \tex_ifnum:D \tex_number:D #2 = \c_true_bool
>      \c_true_bool
>    \tex_else:D
>      \c_false_bool
>    \tex_fi:D
>  \tex_else:D
>    \c_false_bool
>  \tex_fi:D
> }
> \cs_set_nopar:Npn \bool_or_p:nn #1#2 {
>  \tex_ifnum:D \tex_number:D #1 = \c_true_bool
>    \c_true_bool
>  \tex_else:D
>    \tex_ifnum:D \tex_number:D #2 = \c_true_bool
>      \c_true_bool
>    \tex_else:D
>      \c_false_bool
>    \tex_fi:D
>  \tex_fi:D
> }
> \cs_set_nopar:Npn \bool_xor_p:nn #1#2 {
>  \tex_ifnum:D \tex_number:D #1 = \c_true_bool
>    \tex_ifnum:D \tex_number:D #2 = \c_true_bool

Just test for equality: If equal then false, otherwise true. This way
you also avoid expanding the second test twice.

>      \c_false_bool
>    \tex_else:D
>      \c_true_bool
>    \tex_fi:D
>  \tex_else:D
>    \tex_ifnum:D \tex_number:D #2 = \c_true_bool
>      \c_false_bool
>    \tex_else:D
>      \c_true_bool
>    \tex_fi:D
>  \tex_fi:D
> }







-- 
Morten

ATOM RSS1 RSS2