Consider the code in LaTeX3. It tests whether a string is a hexadecimal string (optionally also testing the length of that string) (a string where every character is either 0-9 or A-F).
\documentclass[10pt]{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\ishexadecimal}{o m m m}{
\is_hexadecimal_wrapper:eeTF{#1}{\tl_trim_spaces:e{#2}}{#3}{#4}
}
\str_const:Nn \hex_character_set{0123456789ABCDEF}
\cs_new:Nn \is_hexadecimal_wrapper:nnTF{
\is_hexadecimal:eeTF{#1}{\str_uppercase:n{#2}}{#3}{#4}
}
\cs_new:Nn \is_hexadecimal:nnTF{
\bool_lazy_or:nnTF{\tl_if_novalue_p:n{#1}}{
\int_compare_p:nNn{\str_count:n{#2}}={#1}
}{
\bool_set_true:N \l_tmpa_bool
\str_map_function:nN{#2}{\is_hexadecimal_loop:n}
\bool_if:NTF \l_tmpa_bool{#3}{#4}
}{#4}
}
\cs_new:Nn \is_hexadecimal_loop:n{
\str_if_in:NeF \hex_character_set{#1}{
\bool_set_false:N \l_tmpa_bool
\str_map_break:
}
}
\cs_generate_variant:Nn \str_if_in:NnF{NeF}
\cs_generate_variant:Nn \is_hexadecimal:nnTF{eeTF}
\cs_generate_variant:Nn \is_hexadecimal_wrapper:nnTF{eeTF}
\ExplSyntaxOff
\begin{document}
\ishexadecimal{00AAFF98}{TRUE}{FALSE}% Test if the string is a hexadecimal string: TRUE
\ishexadecimal[6]{00AAFF98}{TRUE}{FALSE}% Test if the string is a hexadecimal string of length 6: FALSE
\ishexadecimal[8]{00AAFF98}{TRUE}{FALSE}% Test if the string is a hexadecimal string of length 8: TRUE
\end{document}
I have several questions on how to make that code better, especially to better understand LaTeX3, protection, and expandability:
QUESTION 1: Are there easy and obvious optimizations (including unnecessary step) that can be done to help compilation time (if this function is low level and called thousands of time in a document)?
QUESTION 2: Should any of the defined function be protected? If so, why? If so, what would the final code look like?
QUESTION 3: When I call \token_if_expandable:NTF
on any of the defined functions in that document, it always return true. Is that the case? Are all the functions fully expandable?
QUESTION 4: If all functions are expandable, should I make \ishexadecimal
a \NewExpandableDocumentCommand
? Is so why, if not, why?
QUESTION 5: Should I use \tl_trim_spaces:e
or >{\TrimSpaces}
? What would be the best practice and why?
An improved version of the code applying good practices (with explanation) would be very nice to understand how to better code in LaTeX3.
EDIT:
QUESTION 6: If the only reason why this function is not expandable is the need for assignment in the context of \str_map_function
(\bool_set_true:N \l_tmpa_bool
) is there an usual way to rewrite this kind of pattern to avoid the use of assignment?
interface3.pdf
should be definedprotected
(there are very few exemptions from this rule). Everything that needs an assignment (except for assignments done byl3flag
) should be definedprotected
. Everything that uses a function that is definedprotected
should be definedprotected
. (there are very few exemptions from these rules)\token_if_expandable:NTF
does a very limited test and not a full analysis of a macro's/function's definition and is no reliable test whether your function should be definedprotected
. All macros that are not definedprotected
are reported expandable by it.expl3
naming conventions. Notably all functions and variables miss the module part in their name, and variables miss their scope and type. A variable should be named\<scope>_(_)<module>_<description>_<type>
with<scope>
being one ofl
,g
andc
, and the second underscore after that present for internal variables, public ones only have one. Functions are named\(__)<module>_<description>:<args>
with the leading two underscores present for internals.