Now Reading
LaTeX3: Programming in LaTeX with Ease

LaTeX3: Programming in LaTeX with Ease

2023-07-31 09:23:59

Many individuals view LaTeX as a typesetting language and overlook the significance of programming in
doc era course of. As a matter of truth, many giant and structural paperwork can profit
from a programming backend, which reinforces format standardization, image coherence, enhancing velocity
and lots of different facets. Regardless of the very fact the usual LaTeX (LaTeX2e) is already Turing full, which
means it’s able to fixing any programming job, the design of many programming interfaces is extremely
inconsistent attributable to compatibility issues. This makes programming with LaTeX2e very difficult and
tedious, even for seasoned pc programmers.

To make programming in LaTeX simpler, the LaTeX3 interface is launched, which goals to supply
modern-programming-language-like syntax and library for LaTeX programmers. Sadly, there may be
little materials concerning this excellent language. After I began studying it, I needed to undergo
its advanced technical handbook, which is time-consuming. Subsequently, I resolve to put in writing a LaTeX3 tutorial
that’s easy-to-understand for generic programmers.

Preface

Why LaTeX3?

Deal with macro growth like a boss

Essentially, works by doing macro substitution: instructions are substituted by their definition, which is subsequently changed by definition’s definition, till one thing irreplaceable is reached (e.g. textual content). For instance, within the following instance, myname is substituted by mynameb; mynameb is then substituted by mynama; and ultimately, mynamea is changed by John Doe, which can’t be expanded anymore. This course of is known as growth.

newcommand{mynamea}{John Doe}
newcommand{mynameb}{mynamea}
newcommand{myname}{mynameb}
My identify is myname.

Most command we use on a regular basis has difficult definitions. Throughout compilation, they are going to be expanded recursively till textual content or primitive is reached. This course of sounds fairly simple, till we need to change the order of macro growth.

Why do we have to change the order of macro growth? Let’s take into account the uppercase macro in , which turns lowercase letters into uppercase ones. However take into account the next case, the place we attempt to apply uppercase to letters abcd and a command cmda. Since cmda expands to abcd, we count on the end result to be ABCDABCD. In actuality, provides us ABCDabcd, which suggests the content material of cmda is unchanged.

newcommand{cmda}{abcd}
uppercase{abcdcmda} %ABCDabcd

How can this occur? Throughout the growth of uppercase, the command scans the merchandise contained in the adjoining curly braces one after the other. If an English letter is encountered, an uppercase counterpart is left within the output stream; in any other case, the unique merchandise is left within the enter stream. When it’s cmda’s flip, as a result of it’s a command as an alternative of a letter, it’s left untouched within the output stream, which is expanded to abcd later.

What if we need to capitalize the whole lot contained in the curly braces? That will require the macro cmda to be expanded earlier than uppercase, or equivalently, altering the order of macro growth. The classical method of doing so in is through expandafter. Sadly, the utilization of expandafter is extraordinarily difficult: in a string of n tokens, to broaden the ith token, there have to be (2^{n-i}-1) expandafter’s earlier than the ith token. Under is a example of how unhealthy this will appear like:

documentclass{article}
start{doc}

defx#1#2#3#4{%
  defarga{#2}%
  defargb{#3}%
  defargc{#4}%
  expandafterexpandafterexpandafterexpandafterexpandafterexpandafterexpandafter#1%
    expandafterexpandafterexpandafterexpandafterexpandafterexpandafterexpandafter
      {expandafterexpandafterexpandafterargaexpandafterexpandafterexpandafter}%
        expandafterexpandafterexpandafter{expandafterargbexpandafter}expandafter
          {argc}}

defy#1#2#3{detokenize{#1#2#3}}

xy{arg1}{arg2}{arg3}

finish{doc}

Clearly, it’s nowhere close to decency: the extreme variety of expandafter’s are generally known as “expandafter purgatory”. Because of this, one of many options of is to supply easy and dependable growth management.

Messy interfaces in LaTeX

Consider it or not, is ready to obtain the whole lot different generic programming languages can do (e.g. C++, Python, Java). Nevertheless, the operate name conventions could be wildly distinct throughout completely different duties; some comparable functionalities could be independently carried out varied packages. Listed here are some examples:

  • File learn
    newreadfile
    openinfile=myfilename.txt
    loopunlessifeoffile
        readfile tofileline % Reads a line of the file into fileline
        % Do one thing with fileline
    repeat
    closeinfile
    
  • File write
    newwritefile
    immediateopenoutfile=myfilename.txt
    immediatewritefile{A line of textual content to put in writing to the file}
    immediatewritefile{One other line of textual content to put in writing to the file}
    closeoutfile
    
  • Integer arithmetic
    newcountmycount
    mycount=numexpr(25+5)/3loosen up
    advancemycount by -3
    multiplymycount by 2
    
  • Situation
    % command-related if assertion
    ifxmycmdundefined
    undefed
    else
      ifmycmd1
      defed, 1
      else
      defed
      fi
    fi
    
    % number-related if assertion
    ifdim#1pt=#2pt
        Equal.
    else%
        Not equal.
    fi%
    
  • Loop

    % use loop
    newcountfoo
    foo=10
    loop
      message{thefoo}
      advance foo -1
    ifnum foo>0
    repeat
    
    % whereas loop (offered by `ifthen` package deal)
    newcounter{ct}
    setcounter{ct}{1}
    whiledo {worth{ct} < 5}%
    {
      thect
      stepcounter {ct}%
    }
    
    % for loop (offered by `ifthen` package deal)
    forloop{ct}{1}{worth{ct} < 5}%
    {%
      thect
    }
    

These inconsistencies set a excessive bar for brand spanking new customers and make it tough to attach a number of elements collectively, even for skilled programmers. Subsequently, goals to supply standardized programming interfaces and documentation for the language.

Objectives of LaTeX3

  • Modernize the syntax of
  • Simplify macro growth management
  • Unify the interfaces throughout varied elements
  • Present standardized libraries for packages (e.g. floating-point arithmetic, common expression, random quantity, high-performance array, and so forth.)

LaTeX3 Naming Conventions (I-1)

Within the following code snippet, we declare a variable vara and a operate cmda. The way in which we distinguish between a variable and a operate is just by judging whether or not the command absorbs arguments or not. Nevertheless, the truth that they’re all known as “instructions” and created with newcommand displays that they’re essentially the identical for system.

newcommand{vara}{it is a variable}
newcommand{cmda}[1]{it is a command: #1}

From customers’ perspective, it is very important separate variables from capabilities as a result of their usages are completely different. Subsequently, our solely possibility is to encode this data into the identify of instructions, in order that customers can differentiate variables and capabilities with little effort. For this reason we have to introduce the naming conference. Earlier than truly elaborating on naming type, I want to make a small diversion and introduce class code first.

Class code and command names

In , each character that we enter is related to a class code. Normal class code task could be seen within the following table:

Class code Description Normal /
0 Escape character-tells to start out searching for a command
1 Begin a bunch {
2 Finish a bunch }
3 Math shift-switch in/out of math mode $
4 Alignment tab &
5 Finish of line ASCII code 13 (r)
6 Macro parameter #
7 Superscript for typesetting math ^
8 Subscript for typesetting math _
9 Ignored character ASCII 0
10 Spacer ASCII codes 32 (house) and 9 (tab character)
11 Letter A…Z, a…z
12 Different 0…9 plus @,.;?” and lots of others
13 Energetic character Particular class code for creating single-character macros equivalent to ~
14 Remark character-ignore the whole lot that follows till the tip of the road %
15 Invalid character, not allowed to look within the .tex enter file ASCII code 127 (127)

When encounter a personality with class 0 (e.g. ), it proceed to scan the following characters, which ultimately ends in one of many following:

  1. Multi-letter instructions: the character following instantly after the escape character has class code 11 (letter). All subsequent characters which have class code 11 are thought-about to type the identify of a command (management phrase). will cease searching for characters that type a part of a command identify when it detects any character that doesn’t have class code 11—equivalent to an area character with class code 10.
  2. Single-letter instructions: the character following instantly after the escape character doesn’t have class code 11.

This mechanism exhibits why we can’t put Arabic numerals or punctuations into command names. Curiously, the class code related to a selected character is mutable. That’s the explanation why most hidden instructions in have @ of their names, as a result of the class code of @ is often 12 (different), which is illegitimate in command names. With a purpose to entry these instructions, we have to name makeatletter, which simply because the identify suggests, modifications the class code of @ to 11 (letter). After utilizing hidden instructions, we have to name makeatother to reset class code task.

In , command names are made up of English letters, underline (_) and colon(:). With a purpose to activate the completely different naming scheme of , one must enter mode with ExplSyntaxOn and exits with ExplSyntaxOff. Basically, ExplSyntaxOn will make the next modifications:

  • The class code of _ and : will probably be set to 11 (letter)
  • All spacers and line breaks will probably be ignored
  1. As a result of the class code of _ modifications from 8 to 11, one can’t use _ to indicate subscripts in math mode. The _ character with class code 8 is given by c_math_subscript_token in .
  2. There are two methods to indicate clean house in : character ~ or escaping house ( ).

Title of variables

Title of capabilities

Once we write C/C++ code, we have to specific declare the kind of every arguments, for instance:

int mult(int a, int b){
  return a * b;
}

To extend the readability of code, the same design is adopted: detailed details about every argument is laid out in <arg-spec>.

It’s value mentioning that each one these naming conventions are merely a suggestion: generally, is not going to prase the identify of the command to amass data. There’s primarily no arduous restriction on the identify of variables and capabilities: the scope or public/non-public identifiers are purely for the sake of customers, not the compiler. Nevertheless, utilizing constant naming conference can improve code readibility.

Studying LaTeX3 Documentation

At this second, most supplies are compiled in The LaTeX3 Interfaces. The primary chapter of this doc briefly introduces the basics of . Every subsequent chapter elaborates on a module of . Features are grouped in numerous sections based mostly on their functions.

Perform documentation

Most objects in sections are detailed descriptions about capabilities. Take tl_set:Nn for example:

tl-set-doc
  • All variants of a operate is listed in field on the left. In accordance with the screenshot above, the next capabilities are offered by :
    tl_set:Nn
    tl_set:NV
    tl_gset:Nx
    tl_gset:cx
    
  • The syntax of a operate is on the top-right.
  • The detailed description of a operate is on the bottom-right.

Scratch variables

Many modules include predefined “scratch variables” in order that customers don’t have to declare any variable when the code is small. In each chapter, their is a devoted part to doc what scratch variables (in addition to constants) are outlined.

tl-set-doc

When writing critical code, it is suggested to keep away from scratch variables to maximise compatibility.

Constants

Some libraries include pre-defined constants. They are going to be launched in a devoted part.

tl-set-doc
tl-set-doc

Abstract

Features & Variables

Defining and utilizing variables

Every module of might use completely different variable development format. Subsequently, it is very important initialize every variable sort with its devoted operate. Basically, operate ends in new are for declaring new variables; capabilities that comprises set or gset are for modifying variables’ states; capabilities that comprises get are for buying variables’ states.

Contemplate two particular instances tl_set:Nn and tl_gset:Nn, that are each used for modifying a token record’s worth. What are the variations? Because it seems, the letter g in gset stands for “world”: often, solely units the worth of a variable regionally, i.e. inside its personal group. That’s to say, the modified worth is not going to be seen exterior the group. Subsequently, if we want a change to be accessible for all capabilities, we have to use gset variants.

A concrete instance:

ExplSyntaxOn
tl_set:Nn l_tmpa_tl {A}
group_begin:
tl_set:Nn l_tmpa_tl {B}
par worth~inside~group:~tl_use:N l_tmpa_tl
group_end:
par worth~exterior~group:~tl_use:N l_tmpa_tl
tl_set:Nn l_tmpb_tl {A} group_begin: tl_gset:Nn l_tmpb_tl {B} par worth~inside~group:~tl_use:N l_tmpb_tl group_end: par worth~exterior~group:~tl_use:N l_tmpb_tl ExplSyntaxOff

The output is:

worth inside group: B
worth exterior group: A
worth inside group: B
worth exterior group: B

It may be seen that tl_set:Nn solely modifies the worth contained in the group and leaves the worth exterior untouched; whereas tl_gset:Nn modifications each values.

Basically, the ideas of utilizing variables are:

  1. Decide the proper variable sort and name the corresponding declaration operate (if the variety of wanted variables is small, conside utilizing scratch variables).
  2. Decide the scope and identify the variable in keeping with naming conventions.
  3. Use set or gset capabilities to switch a variable’s worth.
  4. Use corresponding library capabilities to function on variables.

Declaring capabilities (IV-3.2)

In , cs_set:Npn is often used for declaring capabilities. Aside from it, there are additionally different three capabilities that serve this job, particularly cs_set_nopar:Npn, cs_set_protected:Npn and cs_set_protected_nopar:Npn. As a result of cs_set:Npn is used generally, we primarily put our deal with it. The truth is, their usages are extraordinarily shut.

The process of declaring a operate is as follows:

  1. Decide the variety of arguments and their corresponding sorts ( macros can accpet at most 9 arguments)
  2. Title the operate in keeping with naming conference and outline the operate with certainly one of capabilities above.

For instance, suppose we’re to create a operate that concatenates its two arguments with comma. Subsequently, we all know the variety of arguments is 2, and each arguments are of sort n. Because of this, we will identify the operate my_concat:nn. We are able to outline my_concat:nn like so:

ExplSyntaxOn
%outline my_concat:nn
cs_set:Npn my_concat:nn #1#2 {
    #1,~#2
}
%use my_concat:nn
my_concat:nn {a}{b} %outcome: a, b
ExplSyntaxOff

Copying the definition of present capabilities

Typically, it’s handy to repeat the definition of present capabilities. This may be achieved by invoking cs_set_eq:NN. Within the following instance, we create a model of part operate: my_section:n, after which use it to declare a brand new “Hiya World” part. As we’ll present later, if a operate is said utilizing naming conference, its macro growth management will probably be extra handy.

ExplSyntaxOn
cs_set_eq:NN my_section:n part
my_section:n {Hiya~World}
ExplSyntaxOff

Displaying the definition of capabilities

It’s potential to indicate the definition of a operate by utilizing cs_meaning:N. For instance, cs_meaning:N part
provides:

lengthy macro:->@startsection {part}{1}{z@ }{-3.5ex @plus -1ex @minus
-.2ex}{2.3ex @plus .2ex}{normalfont Giant bfseries }

Abstract

  • Variables of various sorts require separate declaration and operation capabilities.
  • Usually, capabilities with new are for creating new variables; capabilities with set or gset are used to vary the state of variables; capabilities with get are used to amass the state of variables.
  • set capabilities are used to switch variables regionally; gset capabilities are sed to switch variables globally.
  • In , capabilities are often outlined with cs_set:Npn.
  • Utilizing naming conventions can facilitate macro growth management.
  • Use cs_meaning:N to indicate the definition of variables.

Macro Enlargement Management (V)

Again to the uppercase instance above:

newcommand{cmda}{abcd}
uppercase{abcdcmda} %ABCDabcd

To indicate how macro growth could be resolved with , we first create a equal for the operate, particularly my_uppercase:n. At this level, the conduct of my_uppercase:n is similar as uppercase.

newcommand{cmda}{abcd}
ExplSyntaxOn
cs_set_eq:NN my_uppercase:n uppercase
my_uppercase:n {abcdcmda} % ABCDabcd
ExplSyntaxOff

Now, we focus on two methods to manipulation macro growth in order that the output turns into ABCDABCD (as an alternative of ABCDabcd).

Technique 1: change argument specification of capabilities

At this second, the argument sort of my_uppercase:n is n, which signifies an unexpanded token record. As a matter of truth, each operate has n or N sort arguments when first declared. Now, we want to change to sort signature to x, i.e. increasing the whole lot within the token record recursively earlier than being handed to my_uppercase. In , there’s a operate devoted to altering the argument specification of different capabilities: cs_generate_variant:Nn. It takes two arguments: the primary one is the operate we want to modify; the second is the brand new argument specification. Given my_uppercase:n, we will generate my_uppercase:x with the assistance of cs_generate_variant:Nn after which invoke the brand new operate variant.

newcommand{cmda}{abcd}
ExplSyntaxOn
cs_set_eq:NN my_uppercase:n uppercase
cs_generate_variant:Nn my_uppercase:n {x}
my_uppercase:x {abcdcmda} % ABCDABCD
ExplSyntaxOff

Essential Discover: cs_generate_variant:Nn solely works for capabilities following naming conference.

Technique 2: use exp_args:N capabilities (V.4, V.5, V.6)

Declaring new variants with cs_generate_variant:Nn ceaselessly could also be a bit inconvenient. Thankfully, supplies a collection of exp_args:N capabilities that may facilitate macro growth management when the variety of arguments in small.

In brief, if we use cs_generate_variant:Nn to generate and use a brand new variant operate:

cs_generate_variant:Nn func:abcd {efgh}
func:efgh {1}{2}{3}{4}

Will probably be equal to the next exp_args:N operate name:

exp_args:Nefgh func:abcd {1}{2}{3}{4}

Utilizing exp_args:N capabilities, we will additionally absolutely broaden the argument for my_uppercase:n:

newcommand{cmda}{abcd}
ExplSyntaxOn
cs_set_eq:NN my_uppercase:n uppercase
exp_args:Nx my_uppercase:n {abcdcmda} %ABCDABCD
ExplSyntaxOff

It’s value noticing that exp_args:N capabilities can be utilized to manage growth partially. For instance, if a operate takes three arguments, and we apply exp_args:Nc to it, then solely the primary argument will probably be modified, whereas the remainder are left untouched. Within the instance beneath, we apply c sort exansion to the primary argument of NewDocumentCommand from xparse package deal, which permits us to declare a command named after the content material saved in a variable.

% load `xparse` package deal for this instance (will probably be robotically loaded for newer TeX variations)
ExplSyntaxOn
% retailer command identify in a variable
tl_set:Nn l_tmpa_tl {mycmd}

% use exp_args:Nc to broaden the primary arguemnt solely
% which permits us to declare a command utilizing the content material of l_tmpa_tl
exp_args:Nc NewDocumentCommand{l_tmpa_tl}{m}{
  par you~entered~#1
}

% you entered one thing
mycmd{one thing}
ExplSyntaxOff

Abstract

  • Two methods to manage macro growth in : cs_generate_variant:Nn and exp_args:N capabilities
  • cs_generate_variant:Nn solely works on capabilities utilizing naming conference; exp_args:N collection could be utilized to arbitrary capabilities
  • Since solely supplies restricted variety of exp_args:N capabilities, one has to fall again to cs_generate_variant:Nn when the argument mixture doesn’t exist.
  • The arguments of user-defined capabilities are often of sort n or N. We are able to leverage the approaches launched above to switch argument sorts.
  • x sort growth might not work in TikZ environments. One can use edef as an substitution.

LaTeX3: Token Listing and String

Token record (VII)

Every little thing that’s entered in a tex file could be interpreted as a token. Subsequently, token lists are collections of all objects acknowledged by the compiler. In , token lists are probably the most basic and ceaselessly used variable sort.

Setting up a command in token record

Suppose we want to name part*{<title>}, and the <title> is saved within the token record variable l_tmpa_tl. We are able to do it as follows:

ExplSyntaxOn
% put the title in l_tmpa_tl
tl_set:Nn l_tmpa_tl {My~Title}
% assemble the command in l_tmpb_tl
tl_set:Nx l_tmpb_tl {exp_not:N part* {l_tmpa_tl}}
cs_meaning:N l_tmpb_tl % macro:->part *{My Title}
% place the content material of l_tmpb_tl into the enter stream
tl_use:N l_tmpb_tl
ExplSyntaxOff

On this case, we’re utilizing tl_set:Nx, which suggests the whole lot contained in the curly braces will probably be expanded utterly and recursively. Because of this, within the definition of l_tmpb_tl, the variable identify l_tmpa_tl will probably be changed by its worth (expanded recursively). Since we don’t need to broaden the definition of part, we use exp_not:N to suppress its growth.

The x growth may trigger issues when the content material of l_tmpa_tl comprises instructions.
If we solely need to put the worth of l_tmpa_tl surrounded by curly braces in l_tmpb_tl, we will use exp_not:V in tl_set:Nx as follows:
tl_set:Nx l_tmpb_tl {exp_not:N part* {exp_not:Vl_tmpa_tl}}.
When being expanded, the exp_not:V command will place the worth of the subsequent variable within the output and forestall the worth from being additional expanded.

Scholar administration system

Suppose we need to arrange an inside scholar administration system in . We want to implement the next three instructions:

  • scholar: add a brand new scholar into the system
  • allstudent: present all college students, separated by commas
  • thestudent: takes one argument and exhibits the (i)-th scholar

We’ll reuse this instance many occasions all through this tutorial, however with completely different implementation methods. Right here, we use token record associated capabilities to implement the three instructions above.

documentclass{article}
usepackage[T1]{fontenc}
usepackage{expl3}
usepackage{amsmath, amssymb}

start{doc}

ExplSyntaxOn
% shops all college students, separated by commas
tl_new:N l_student_comma_tl
% shops the identify of every scholar
tl_new:N l_student_group_tl
newcommand{scholar}[1]{
    #1% outputs scholar's identify
    % examine if l_student_comma_tl is empty
    % it is a conditional department assertion
    % which we'll focus on within the subsequent sections
    tl_if_empty:NTF l_student_comma_tl {
        % if empty, don't prepend comma earlier than identify
        tl_put_right:Nn l_student_comma_tl {#1}
    } {
        % in any other case, prepend comma earlier than identify
        tl_put_right:Nn l_student_comma_tl {,~#1}
    }
    % put scholar identify in a bunch and
    % retailer it in l_student_group_tl
    tl_put_right:Nn l_student_group_tl {{#1}}
}
newcommand{allstudent}{
    % outputs l_student_comma_tl
    tl_use:N l_student_comma_tl
}
newcommand{thestudent}[1]{
    % outputs the #1-th token in l_student_group_tl
    tl_item:Nn l_student_group_tl {#1}
}
ExplSyntaxOff

% John and Lisa and David and Emily
scholar{John} and scholar{Lisa} and scholar{David} and scholar{Emily}

% John, Lisa, David, Emily
parallstudent

% Emily and David and Lisa and John
parthestudent{4} and thestudent{3} and thestudent{2} and thestudent{1}

finish{doc}
  • On this resolution, we retailer every identify twice in l_student_comma_tl and l_student_group_tl. l_student_comma_tl shops the identify of all college students, joined by commas, which is utilized by allstudent. l_student_group_tl permits index entry for scholar names, for every scholar is saved as a bunch within the token record. Each time one calls scholar, the brand new scholar identify will probably be inserted into the 2 token lists.
  • When inserting into l_student_comma_tl, there isn’t any must prepend comma if it’s the first identify. Subsequently, we have to use the conditional assertion tl_if_empty:NTF to specify this conduct.
  • Discover that we encompass the coed identify with curly braces when inserting into l_student_group_tl, which successfully encapsulates every scholar identify inside a bunch. Because of this, when calling tl_item:Nn, the complete group will probably be returned, which permits us to retrieve the coed identify as an entire.

String (VIII)

A detailed relative to token record is string. Once we apply tl_use:N to a token record variable, it’s equal to typing its content material straight within the tex file. If we run the next instance

newcommand{cmda}{efgh}
ExplSyntaxOn
tl_set:Nn l_tmpa_tl {abcdcmda}
tl_use:N l_tmpa_tl %abcdefgh
ExplSyntaxOff

then we’ll get abcdegfh within the doc output, as a result of cmda is saved as a command in l_tmpa_tl, which is subsequently expanded to efgh. Nevertheless, If we run the identical instance with string sort, then the whole lot contained in the string variable will probably be interpereted as textual content as an alternative of command of particular character. Consequently, the output within the doc turns into abcdcmda.

newcommand{cmda}{efgh}
ExplSyntaxOn
str_set:Nn l_tmpa_str {abcdcmda}
str_use:N l_tmpa_str %abcdcmda
ExplSyntaxOff

We are able to use tl_to_str:n to transform a token record right into a string. It’s potential to rework strings again to token lists with tl_rescan:nn.

Vertical textual content node in TikZ

ExplSyntaxOn
cs_set:Npn my_vert_str:n #1 {
  % retailer argument as string
  str_set:Nn l_tmpa_str {#1}
  % traverse the string
  str_map_inline:Nn l_tmpa_str {
      % middle every character at their very own line
      centering ##1 par
  }
}
% declare latex interface
newcommand{vertstr}[1]{
  my_vert_str:n {#1}
}
ExplSyntaxOff

start{tikzpicture}
node[draw=black, text width=1cm] {vertstr{ab$c$d~\}};
finish{tikzpicture}

Output:

’s string methodology is carried out with detokenize. Because of this, neither tl_to_str:n nor str_set:Nn can assure that the string output is precisely the identical as customers’ enter. For instance, detokenize provides an additional house after instructions. That’s, verb|abc| turns into verb |abc|. This may be tough in some eventualities.

Very ceaselessly, we have to evaluate if two token lists or strings are equal. For the reason that token record library and string library each have their very own equality capabilities, we will select between tl_if_eq: (from token record library) and str_if_eq: (from string library). Except it’s completely vital, it is suggested to make use of string library’s comparision capabilities. That’s as a result of tl_if_eq: not solely checks if the characters are the identical, but it surely additionally checks if the class code of every character is similar. Because of this, two seemingly an identical variables may end up in False end result when utilizing tl_if_eq:.

LaTeX3: Numeric Analysis and Boolean Logic

This part primarily consists of code snippets with detailed feedback, for the underlying methodology of those subjects are just like different programming languages. Subsequently, it’s extra helpful to have the ability to find the proper APIs in documentation.

Boolean logic (XIII)

ExplSyntaxOn
% declare new boolean worth
bool_new:N l_my_bool
% set to true
bool_set_true:N l_my_bool
% set to false
bool_set_false:N l_my_bool
% boolean based mostly conditional assertion
bool_if:nTF {l_my_bool} {true} {false} %false
% boolean based mostly whereas loop
bool_do_while:nn {l_my_bool} {}
% boolean based mostly till loop
% boolean capabilities assist C/C++
% type !, || and && operations
bool_do_until:nn {!l_my_bool} {}
ExplSyntaxOff

Integer arithmetic

Implementing modulo operation

It’s value noticing that already has int_mod:nn. This pattern is for demonstration functions.

ExplSyntaxOn
cs_set:Npn my_mod:nn #1#2 {
  % retailer #1//#2 in l_tmpa_int
  int_set:Nn l_tmpa_int { int_div_truncate:nn {#1}{#2} }
  % compute (#1)-l_tmpa_int*(#2)
  % make sure that to encompass operands with parentheses
  % in order that when #1 is an expression (e.g. 3-2)
  % the order of arithmetic is not going to change
  int_eval:n { (#1) - l_tmpa_int * (#2) }
}
% outline LaTeX interface
newcommand{mymod}[2]{
  my_mod:nn {#1} {#2}
}
ExplSyntaxOff
mymod{5}{3}mymod{6}{3}mymod{7}{1+2}%201

Implementing Caesar cipher

Caesar cipher is a basic substitution cipher in cryptography.

ExplSyntaxOn
cs_set:Npn my_caesar_cipher:n #1 {
  % remodel #1 to decrease case and retailer in l_tmpa_str
  str_set:Nx l_tmpa_str {tl_lower_case:n {#1}}
  % clear l_tmpb_str to retailer outcomes
  str_clear:N l_tmpb_str
  % str_map_inline:Nn traverses the string
  % and cross every character as first argument
  str_map_inline:Nn l_tmpa_str {
      % `##1 provides the ASCII code of ##1
      % 91 is the ASCII code of 'a'
      % this permits us to compute the offset of ##1
      int_set:Nn l_tmpa_int { int_eval:n {`##1 - 97} }
      % suppose the shifting of our Ceaser cipher is 3
      int_set:Nn l_tmpb_int { int_mod:nn {l_tmpa_int + 3}{26} }
      % place new character in l_tmpb_str
      str_put_right:Nx l_tmpb_str {
          % this operate generates a personality given
          % character code and class code
          % as a result of we're coping with English letters
          % the class code is 11
          char_generate:nn {l_tmpb_int + 97}{11}
      }
  }
  % outputs l_tmpb_str
  str_use:N l_tmpb_str
}
my_caesar_cipher:n {helloworld}%khoorzruog
ExplSyntaxOff

Integer-based loop and situation

Widespread integer-based conditional statements:

  1. int_compare_p: collection: evaluate two integers given a relation and returns a boolean worth
  2. int_compare: collection: evaluate two integers given a relation and execute T code or F code based mostly on the outcome

Widespread integer-based loops:

  1. int_do_while: collection
  2. int_do_until: collection
  3. int_step_function:, int_step_inline: and int_step_variable:

1, 2 are sometimes used with int_incr:N (int_gincr:N) and int_decr:N (int_gdecr:N)

One might have seen that the majority integer associated comparions present :n and :nNn` variants. They’re completely different within the following methods:

  • :nNn solely helps three forms of comparsion: <, > and =
  • Along with <, > and ==, :n additionally helps >=, <= and !=
  • :n helps chained comparability: a<b<c
  • The velocity of :n is about one fifth of the velocity of :nNn
int_compare_p:nNn {l_tmpa_int} < {l_tmpb_int}
% is equivalant to 
int_compare_p:n {l_tmpa_int < l_tmpb_int}

Computing the best widespread divisor (non-recursive)

We implement the Eulidean algorithm to compute the best widespread divisor.

ExplSyntaxOn
% declare yet another scratch variable
int_new:N l_tmpc_int
cs_set:Npn my_gcd:nn #1#2 {
  % put #1 in l_tmpa_int
  int_set:Nn l_tmpa_int {#1}
  % put #2 in l_tmpb_int
  int_set:Nn l_tmpb_int {#2}
  % loop till l_tmpb_int equals 0
  int_do_until:nNnn {l_tmpb_int} = {0} {
      % replace three variables
      int_set:Nn l_tmpc_int { l_tmpb_int }
      int_set:Nn l_tmpb_int { int_mod:nn {l_tmpa_int}{l_tmpb_int} }
      int_set:Nn l_tmpa_int {l_tmpc_int}
  }
  % outputs l_tmpa_int
  int_use:N l_tmpa_int
}
my_gcd:nn {6}{3}~my_gcd:nn {270}{192}% 3 6
ExplSyntaxOff

Computing the best widespread divisor (recursive)

As talked about above, in comparison with gset capabilities, set capabilities solely modify variable values inside the present group. Utilizing this mechanism, it’s potential to mimic a callstack in different programming languages to implement recursive algorithms. Within the following instance, it’s proven that native varialbles is not going to be modified by subroutines as a result of every set operate is guarded by group_begin: and group_end:.

ExplSyntaxOn
cs_set:Npn my_gcd_recursive:nn #1#2 {
	group_begin:
	% all variable assignments will probably be constrained on this group
	int_compare:nNnTF {#2} = {0} {
		int_gset:Nn g_tmpa_int {#1}
	} {
		int_set:Nn l_tmpa_int {int_mod:nn {#1}{#2}}
		int_set:Nn l_tmpb_int {int_div_truncate:nn {#1}{#2}}
		exp_args:Nnx my_gcd_recursive:nn {#2} {int_use:N l_tmpa_int}
		% output debug message
		par $int_use:N l_tmpb_int occasions #2 + int_use:N l_tmpa_int = #1$
	}
	group_end:
}
my_gcd_recursive:nn {12546}{156}
par $operatorname{gcd}(12546, 156) = int_use:N g_tmpa_int$
ExplSyntaxOff

Output:

3 × 6 + 0 = 18
1 × 18 + 6 = 24
2 × 24 + 18 = 66
2 × 66 + 24 = 156
80 × 156 + 66 = 12546
gcd(12546; 156) = 6

Scholar administration system

Now, we implement the aforementioned scholar administration system with integer associated capabilities.

ExplSyntaxOn
% used to retailer the identify of every scholar
tl_new:N l_student_group_tl
newcommand{scholar}[1]{
  #1% outputs scholar identify
  % put scholar identify in group after which
  % insert into l_student_group_tl
  tl_put_right:Nn l_student_group_tl {{#1}}
}
newcommand{allstudent}{
  %tl_count:N returns the size of a token record
  %int_step_inline:nn traverses all integers in 
  % the vary (1, #1) and cross the loop variable as
  % #1
  int_step_inline:nn {tl_count:N l_student_group_tl}{
      % get the ##1-th component from l_student_group_tl
      tl_item:Nn l_student_group_tl {##1}
      % decide if it's the final component
      % in any other case, append comma
      int_compare:nNnTF {##1} = {tl_count:N l_student_group_tl} {} {,~}
  }
}
newcommand{thestudent}[1]{
  % outputs the #1-th merchandise in l_student_group_tl
  tl_item:Nn l_student_group_tl {#1}
}
ExplSyntaxOff

% John and Lisa and David and Emily
scholar{John} and scholar{Lisa} and scholar{David} and scholar{Emily}

% John, Lisa, David, Emily
parallstudent

% Emily and David and Lisa and John
parthestudent{4} and thestudent{3} and thestudent{2} and thestudent{1}

3 ways to implement nested loop

ExplSyntaxOn
par
int_step_variable:nNn {4} l_tmpa_tl {
    int_step_variable:nNn {4} l_tmpb_tl{
        (l_tmpa_tl,l_tmpb_tl)
    }
}
par
int_step_inline:nn {4}  {
    int_step_inline:nn {4}  {
        (#1,##1)
    }
}
par
int_set:Nn l_tmpa_int {1}
int_do_while:nNnn {l_tmpa_int} < {5} {
    int_set:Nn l_tmpb_int {1}
    int_do_while:nNnn {l_tmpb_int} < {5} {
        (int_use:N l_tmpa_int,int_use:N l_tmpb_int)
        int_incr:N l_tmpb_int
    }
    int_incr:N l_tmpa_int
}
ExplSyntaxOff

Output:

(1,1)(1,2)(1,3)(1,4)(2,1)(2,2)(2,3)(2,4)(3,1)(3,2)(3,3)(3,4)(4,1)(4,2)(4,3)(4,4)
(1,1)(1,2)(1,3)(1,4)(2,1)(2,2)(2,3)(2,4)(3,1)(3,2)(3,3)(3,4)(4,1)(4,2)(4,3)(4,4)
(1,1)(1,2)(1,3)(1,4)(2,1)(2,2)(2,3)(2,4)(3,1)(3,2)(3,3)(3,4)(4,1)(4,2)(4,3)(4,4)

Drawing a sq. quantity grid in TikZ

tikzset{
  mynode/.type={
    minimal top=1cm,
    minimal width=1cm,
    draw,
    anchor=north west
  }
}

ExplSyntaxOn
start{tikzpicture}
int_step_inline:nn {6} {
  int_step_inline:nn {8} {
    node[mynode] at (##1, -#1) {tiny int_eval:n {(#1 - 1) * 8 + ##1}};
  }
}
finish{tikzpicture}
ExplSyntaxOff

Output:

Floating level quantity (XXIII) and dimension (XX)

The utilization of floating level numbers is just like that of integers: all of them have their corresponding new, set, eval and evaluate capabilities. It’s value noticing that fp_eval:n helps a collection of scientific capabilities, which is demonstrated beneath.

ExplSyntaxOn
fp_set:Nn l_tmpa_fp {2.0}
parfp_eval:n {sqrt(l_tmpa_fp)}% 1.414213562373095
parfp_eval:n {sin(l_tmpa_fp)}% 0.9092974268256817
par fp_eval:n {sin(c_pi_fp)}% 0.0000000000000002384626433832795
ExplSyntaxOff

It’s value noticing that ’s floating level library is written in pure , which suggests it differs from IEEE 754 floating level numbers essentially. Nonetheless, after a collection of experiments, it’s proven that l3fp’s arithmetic accuracy is sort of an identical to IEEE 754 floating level. The discrapency is neglectable in on a regular basis eventualities .

Floating factors are just like dimensions, besides that dimensions are floating level numbers with a unit (often in pt). In , dimension variables are represented utilizing IEEE 754 (single precision) floating level internally. Subsequently, their processing velocity is way sooner than l3fp. Dimension variables and floating level variables could be transformed from each other utilizing dim_to_fp:n and fp_to_dim:n. It’s potential to make use of dimension varialble straight in fp_eval:n. On this case, dimensions will probably be transformed into pt and lose their unit.

Drawing an oblong quantity grid in TikZ

ExplSyntaxOn
% set width and top of every cell
dim_new:N l_w_dim
dim_set:Nn l_w_dim {1.2cm}
dim_new:N l_h_dim
dim_set:Nn l_h_dim {0.5cm}

tikzset{
  mynode/.type={
    minimal~top=l_h_dim,
    minimal~width=l_w_dim,
    draw,
    anchor=north~west
  }
}

start{tikzpicture}
int_step_inline:nn {6} {
  int_step_inline:nn {8} {
    node[mynode] at 
    (fp_eval:n {##1 * l_w_dim} pt, -fp_eval:n {#1 * l_h_dim} pt) 
    {tiny int_eval:n {(#1 - 1) * 8 + ##1}};
  }
}
finish{tikzpicture}
ExplSyntaxOff

Output:

Drawing factors on a circle and join them pairwise

ExplSyntaxOn
start{tikzpicture}
	% draw factors on the circle
	int_step_inline:nn {10} {
		node[fill=black,
        		circle,
        		inner~sep=0pt,
			outer~sep=0pt,
			minimum~width=1mm] (n#1) at (
			fp_eval:n {cos(#1 * 36 * c_one_degree_fp)} cm,
			fp_eval:n {sin(#1 * 36 * c_one_degree_fp)} cm
		) {};
	}
	% join factors pairwise
	int_step_inline:nn {10} {
		int_step_inline:nn {10} {
			draw (n#1)--(n##1);
		}
	}
		
	
finish{tikzpicture}
ExplSyntaxOff

Output:

LaTeX3: Knowledge Construction

Queue (X, XV)

Queues are important within the implementation of many algorithms. Therefore, supplies its queue implementation: l3seq.

ExplSyntaxOn
% create new queue
seq_new:N l_my_seq
% empty queue
seq_clear:N l_my_seq
% push proper into queue
seq_put_right:Nn l_my_seq {howdy}
seq_put_right:Nn l_my_seq {world}
% be part of components with '-' and output the outcome
seq_use:Nn l_my_seq {-} % hello-world
% get the size of queue
seq_count:N l_my_seq % 2
% pop the rightmost merchandise and retailer it in l_tmpa_tl
seq_pop_right:NN l_my_seq l_tmpa_tl
% get the first merchandise within the queue
seq_item:Nn l_my_seq {1}
% traverse objects in queue
% comparable capabilities embody seq_map_inline:
% and seq_map_function:
seq_map_inline:Nn l_my_seq {
    #1
}
ExplSyntaxOff

We name the l3seq container “queue” or “sequence” as an alternative of “array” or “record”. One of many causes for that is that index entry is learn solely: one can solely entry merchandise with seq_item:Nn, however it’s nonetheless not possible to switch an merchandise based mostly on index. Nevertheless, if one needs to create a sequence of integer or floating level numbers, it’s potential to reap the benefits of l3intarray or l3fparray, which permits index-based task. As it will likely be mentioned later, they possess another fascinating qualities.

Scholar administration system

ExplSyntaxOn
% a queue that shops scholar names
seq_new:N l_student_seq
newcommand{scholar}[1]{
  #1% outputs scholar identify
  % push scholar identify to the precise
  seq_put_right:Nn l_student_seq {#1}
}
newcommand{allstudent}{
  % be part of components within the queue with comma
  % after which output the outcome
  seq_use:Nn l_student_seq {,~}
}
newcommand{thestudent}[1]{
  % outputs the #1-th component within the queue
  seq_item:Nn l_student_seq {#1}
}
ExplSyntaxOff

% John and Lisa and David and Emily
scholar{John} and scholar{Lisa} and scholar{David} and scholar{Emily}

% John, Lisa, David, Emily
parallstudent

% Emily and David and Lisa and John
parthestudent{4} and thestudent{3} and thestudent{2} and thestudent{1}

Bracket matching

ExplSyntaxOn
cs_set:Npn my_paren_match:n #1 {
  % convert #1 into string
  str_set:Nn l_tmpa_str {#1}
  % clear working queue
  seq_clear:N l_tmpa_seq
  % boolean variable to be set true if parentheses doesn't match
  bool_set_false:N l_tmpa_bool
  str_map_inline:Nn l_tmpa_str {
      % str_case:nn is just like the "change" assertion in C
      str_case:nn {##1} {
          % ------
          % for left brackets, merely push them into the queue
          {(} {
              seq_put_right:Nn l_tmpa_seq {(}
          }
          {[} {
              seq_put_right:Nn l_tmpa_seq {[}
          }
          % ------
          % ------
          % more work needs to be done for right brackets
          {)} {
              % pop the rightmost element and store it in l_tmpb_str
              seq_pop_right:NN l_tmpa_seq l_tmpb_str
              % compare it with left round bracket
              % notice that the first argument is passed by value
              str_if_eq:VnF l_tmpb_str {(} {
                  % this is executed only when equality does not hold
                  % set "not match" to be true
                  bool_set_true:N l_tmpa_bool
                  % exits current loop
                  str_map_break:
              }
          }
          {]} {
              seq_pop_right:NN l_tmpa_seq l_tmpb_str
              str_if_eq:VnF l_tmpb_str {[} {
                  bool_set_true:N l_tmpa_bool
                  str_map_break:
              }
          }
          % ------
      }
  }
  % see if "not match" is true
  bool_if:NTF l_tmpa_bool {Not~Match} {
      % see if the working queue is empty
      seq_if_empty:NTF l_tmpa_seq {Match} {Not~Match}
  }
}
parmy_paren_match:n {()()} % Match
parmy_paren_match:n {([content()])()[]} % Match
parmy_paren_match:n {([content())()[]} % Not Match
parmy_paren_match:n {([content()])()[} % Not Match
ExplSyntaxOff

A very similar data structure is l3clist, which stands for “comma-separated list”. Most functions provided by l3clist is same as l3seq, except that it provides a convenient constructor that allows one to initialize a sequence with comma-separated content. An example is given below.

ExplSyntaxOn
clist_new:N l_my_clist
clist_set:Nn l_my_clist {This,is,my,list,1,2,3}
clist_use:Nn l_my_clist {-} %This-is-my-list-1-2-3
ExplSyntaxOff

Dictionary (XVII)

also provides key-value access dictionary container: l3prop. It is similar to dict in Python or map in C++.

ExplSyntaxOn
% create new dictionary
prop_new:N l_my_prop
% clear dictionary
prop_clear:N l_my_prop
% add/update key-value pair
prop_put:Nnn l_my_prop {key} {val}
% get value given key
prop_item:Nn l_my_prop {key} % val
% get number of key-value pairs
prop_count:N l_my_prop %1
% traverse key-value pairs
% similar functions include prop_map_function:
% and prop_map_tokens:
prop_map_inline:Nn l_my_prop {
    (#1, #2)
}
% delete key-value pair
prop_remove:Nn l_my_prop {key}
ExplSyntaxOff

Arabic numerals to English (0-99)

ExplSyntaxOn

prop_new:N l_english_prop
prop_set_from_keyval:Nn l_english_prop {
  0=zero,
  1=one,
  2=two,
  3=three,
  4=four,
  5=five,
  6=six,
  7=seven,
  8=eight,
  9=nine,
  10=ten,
  11=eleven,
  12=twelve,
  13=thirteen,
  15=fifteen,
  18=eighteen,
  20=twenty,
  30=thirty,
  40=forty,
  50=fifty,
  80=eighty
}

% extra scratch variable
tl_new:N l_tmpc_tl

cs_set:Npn my_arabic_to_eng:n #1 {
  str_set:Nn l_tmpa_str {#1}
  prop_if_in:NVTF l_english_prop l_tmpa_str {
    % if the number is in the dictionary, output it directly
    % this works for most numbers under 20
    exp_args:NNV prop_item:Nn l_english_prop l_tmpa_str
  } {
    int_compare:nNnTF {#1} < {20} {
      % deal with teens
      exp_args:NNx prop_item:Nn l_english_prop {
        int_eval:n {#1 - 10}
      }
      teen
    } {
      % deal with numbers between 20-99
      % acquire number in tens
      int_set:Nn l_tmpa_int {
        int_div_truncate:nn {#1} {10} * 10
      }
      % acquire number in ones
      int_set:Nn l_tmpb_int {
        #1 - l_tmpa_int
      }
      % #1 = l_tmpa_int + l_tmpb_int
      tl_set:Nx l_tmpa_tl {int_use:N l_tmpa_int}
      
      % outputs the "-ty" word
      prop_if_in:NVTF l_english_prop l_tmpa_tl {
        % no need to construct: get from dict directly
        exp_args:NNV prop_item:Nn l_english_prop l_tmpa_tl
      } {
        % need to construct the "-ty" word
        tl_set:Nx l_tmpc_tl {tl_head:N l_tmpa_tl}
        exp_args:NNV prop_item:Nn l_english_prop l_tmpc_tl
        ty
      }
      % no need to output second digit if it is zero
      int_compare:nNnF {l_tmpb_int} = {0} {
        % otherwise, show second digit
        space
        tl_set:Nx l_tmpb_tl {int_use:N l_tmpb_int}
        exp_args:NNV prop_item:Nn l_english_prop l_tmpb_tl
      }
    }
  }
}

parmy_arabic_to_eng:n {0} % zero
parmy_arabic_to_eng:n {18} % eighteen
parmy_arabic_to_eng:n {53} % fifty three
parmy_arabic_to_eng:n {85} % eighty five

ExplSyntaxOff

A number of containers provide item-based access methods. For example, tl_item:Nn, seq_item:Nn, prop_item:Nn, etc. Unlike in most programming languages, where the complexity of these methods are constant time (or in some cases, logarithmic time), these methods takes linear time in . That is to say, the larger the container is, the longer the average access time will be.

Fundamentally, is a text-based macro language. There is no easy way for scripts to access a computer’s memory space directly. As a result, all containers are essentially constructed with text and requires further interpretation when use. This implies that most containers have extremely high time and memory consumption.

If one wants to store an array of integer or floating point number in , there are two types of high performance containers that allow constant time access, namely l3intarray and l3fparray. In this article, I discussed how to use l3intarray to speed up string reversal.

LaTeX3: Regular Expression (XXVIII)

Regular expression is a powerful tool for pattern matching in text documents. In , the l3regex module provides limited support for standard regular expression syntax. These are some of the frequently used functions in l3regex:

  • regex_new:N: creates new regular expression variable
  • regex_set:Nn: set the content of a regular expression variable
  • All of the following functions take either a raw regular expression or regular expression variable as the first argument. Raw regular expressions surrounded by braces requires compilation before use. The regex_set:Nn function will apply both compilation and storage. Therefore, for a regular exression used multiple times, saving it in a variable may save some time.
  • regex_match:nnTF: match a string based on the regular expression and execute T/F code based on the outcome
  • regex_count:nnN: count the number of matches and store the result in an integer variable
  • regex_extract_once:nnN: extract the first match in the string and store it in a token list variable
  • regex_extract_all:nnN: extract all matches in the string and store them in a queue
  • regex_split:nnN: split the string based on the regular expression and saved the result in a queue
  • regex_replace_once:nnN: replace the first match
  • regex_replace_all:nnN:replace all matches

To allow interaction with , the syntax of l3regex is slightly different from the standard. For more details, please see documentation.

Check if a character is Chinese

ExplSyntaxOn
% create and compile regex
regex_new:N l_chn_regex
% the regular expression for Chinese characters
regex_set:Nn l_chn_regex {[x{3400}-x{9FBF}]}

cs_set:Npn my_is_chn:n #1 {
  % retailer #1 as string
  str_set:Nn l_tmpa_str {#1}
  % clear outcome queue
  seq_clear:N l_tmpa_seq
  % traverse the string
  str_map_inline:Nn l_tmpa_str {
      % examine if the string matches l_chn_regex
      regex_match:NnTF l_chn_regex {##1} {
          % in that case, output Y
          seq_put_right:Nn l_tmpa_seq {Y}
      } {
          % in any other case, output N
          seq_put_right:Nn l_tmpa_seq {N}
      }
  }
  % present all contents within the queue, separated by white house
  seq_use:Nn l_tmpa_seq {house}
}

% Y N Y N
parmy_is_chn:n {中a文b}
% N N N N N N N Y Y Y
parmy_is_chn:n {바탕체ヒラギノ細明體}
ExplSyntaxOff

Substitute a command with one other

Within the following instance, all prevalence of cmda is changed by cmdb. This instance makes use of l3regex’s particular syntax.

newcommand{cmda}[1]{(#1)}
newcommand{cmdb}[1]{[#1]}
ExplSyntaxOn
tl_set:Nn l_tmpa_tl {cmda{X}~cmdb{Y}}
partl_use:N l_tmpa_tl % (X) [Y]
% c will seize command names
regex_replace_all:nnN {c{cmda}} {c{cmdb}} l_tmpa_tl
par tl_use:N l_tmpa_tl % [X] [Y]
ExplSyntaxOff

Generate TikZ image based mostly on a template

tikzset{
  mynode/.type={
    outer sep=0pt
  }
}

ExplSyntaxOn
% that is the template of every node
% which we'll fill with common expressions
tl_new:N l_template_tl
tl_set:Nn l_template_tl {
  node[mynode,@1] (@2) at (@3) {@4}; 
}

% counts the overall variety of nodes
int_new:N l_node_counter_int
int_gset:Nn l_node_counter_int {0}

% #1: type
% #2: angle
% #3: content material
cs_set:Npn my_draw_node:nnn #1#2#3 {
  % set our working variable
  tl_set_eq:NN l_tmpa_tl l_template_tl
  % fill type
  regex_replace_once:nnN {@1} {#1} l_tmpa_tl
  
  % increment counter
  int_gincr:N l_node_counter_int
  % retailer the identify of latest node in l_tmpb_tl
  % node identify is generated with int_to_alph:n
  tl_set:Nx l_tmpb_tl {int_to_alph:n {l_node_counter_int}}
  % fill node identify
  % use u to switch with the content material of a token record
  regex_replace_once:nnN {@2} {u{l_tmpb_tl}} l_tmpa_tl
  
  % calculate the place of the node based mostly on angle
  tl_set:Nx l_tmpb_tl {
    fp_eval:n {3 * cos(#2 * c_one_degree_fp)}, 
    fp_eval:n {3 * sin(#2 * c_one_degree_fp)}
  }
  % fill place
  regex_replace_once:nnN {@3} {u{l_tmpb_tl}} l_tmpa_tl
  
  % fill content material
  regex_replace_once:nnN {@4} {#3} l_tmpa_tl
  
  % output outcome
  tl_use:N l_tmpa_tl
}

start{tikzpicture}
my_draw_node:nnn {circle,draw}{200}{L}
my_draw_node:nnn {draw}{160}{A}
my_draw_node:nnn {circle,draw}{120}{T}
my_draw_node:nnn {draw}{80}{E}
my_draw_node:nnn {circle,draw}{40}{X}
my_draw_node:nnn {draw}{0}{3}

draw (a)--(b)--(c)--(d)--(e)--(f)--(a);
finish{tikzpicture}

ExplSyntaxOff

Output:

Processing multi-line textual content

We are able to use common expressions and the xparse package deal to course of multi-line textual content.
On this instance, we attempt to implement a code itemizing command with line numbering.
If we use +v argument specification in NewDocumentCommand, the argument will probably be captured as multi-line verbatim.
As soon as the argument is captured as #1, we use regex_split:nnN {^^M} {#1} l_tmpa_seq to separate it into a number of strains and save every line in l_tmpa_seq.
The ^^M notation means the carridge return character (ASCII code 13); extra particulars concerning the ^^ notation could be discovered on this link.

ExplSyntaxOn
NewDocumentCommand{numberedtext}{+v}{
    regex_split:nnN {^^M} {#1} l_tmpa_seq % cut up into strains
    int_set:Nn l_tmpa_int {1} % line quantity variable
    seq_map_inline:Nn l_tmpa_seq {
        par
        group_begin: % we need to restrict ttfamily on this group
        int_use:N l_tmpa_int ttfamily  % additional house between line quantity and content material
        tl_to_str:n {##1} % convert content material into string
        group_end:
        int_incr:N l_tmpa_int % increment line quantity counter
    }
}
ExplSyntaxOff

numberedtext{NewDocumentCommand{numberedtext}{+v}{
    regex_split:nnN {^^M} {#1} l_tmpa_seq
    int_set:Nn l_tmpa_int {1}
    seq_map_inline:Nn l_tmpa_seq {
        par
        group_begin:
        int_use:N l_tmpa_int ttfamily  
        tl_to_str:n {##1}
        group_end:
        int_incr:N l_tmpa_int
    }
}}

Output:

LaTeX3: File I/O (XIX)

In , the APIs of file operations are standardized.

File studying:

  • ior_new:N: create new I/O learn variable
  • ior_open:Nn: open file for studying
  • ior_close:N: shut file
  • ior_get:NN: learn one line or an entire group as token record
  • ior_str_get:NN: learn one line as string
  • ior_map_inline:Nn: traverse file as token record
  • ior_str_map_inline:Nn: traverse file as string
  • ior_if_eof_p:N: examine if the tail of a file is reached

File writing:

  • iow_new:N: create new I/O write variable
  • iow_open:Nn: open file for writing
  • iow_close:N: shut file
  • iow_now:Nn: instantly write content material into the file, adopted by a line break
  • iow_newline:: line break function-this operate have to be expanded as a way to take impact (e.g. iow_now:Nx l_tmpa_iow {iow_newline:}; if the second argument has sort n, nothing will occur)

Writing to a file

ExplSyntaxOn
iow_open:Nn g_tmpa_iow {testfile1.txt}
iow_now:Nx g_tmpa_iow {howdyiow_newline: world}
iow_close:N g_tmpa_iow
ExplSyntaxOff

The content material of testfile1.txt:

Studying and parsing comma-separated file

Within the following instance, we retailer a collection of decimal numbers in a comma-separated file. Then, we learn the file and calculate the sum of all numbers.

ExplSyntaxOn
% write numbers to testfile2.txt
iow_open:Nn g_tmpa_iow {testfile2.txt}
iow_now:Nx g_tmpa_iow {1.2,2.6,3.7,4.9,5.0,6.5,7.4,8.2,9.4,10.8}
iow_close:N g_tmpa_iow

% open file for studying
ior_open:Nn g_tmpa_ior {testfile2.txt}
% get the primary line
ior_str_get:NN g_tmpa_ior l_tmpa_str
% create operate variant
cs_generate_variant:Nn regex_split:nnN {nVN}
% cut up the road right into a queue with common expression
regex_split:nVN {,} l_tmpa_str l_tmpa_seq

% initialize the sum variable
fp_set:Nn l_tmpa_fp {0.0}
% traverse the queue
seq_map_inline:Nn l_tmpa_seq {
  % sum the numbers
  fp_add:Nn l_tmpa_fp {#1}
}

% present lead to math mode
$seq_use:Nn l_tmpa_seq {+} = fp_use:N l_tmpa_fp$
% shut file
ior_close:N g_tmpa_ior
ExplSyntaxOff

Output:

1.2+2.6+3.7+4.9+5.0+6.5+7.4+8.2+9.4+10.8=59.7

Memo

Helpful methods:

  • Many modules present present capabilities. They’ll print the content material of variables to the log file, which may be very useful for debug functions.
  • additionally helps producing random numbers or triggering randomized entry to token lists or queues.

As a result of the variety of modules is large, it is extremely tough to cowl most of them in a restricted period of time. Right here, I record another libraries which can be value taking a look at:

  • l3coffins (XXX), l3box (XXIX): permits one to gauge the width/top of objects
  • l3intarray (XXII), l3fparray (XXIV): excessive efficiency numeric arrays
  • l3sort: sotring queues/token lists
  • l3msg: producing exception messages

Finish Word

On this article, I attempt to breifly introduce the naming conference, the utilization of variables and capabilities and a few generally used modules of . I hope that this group permits readers to know the fundamentals of , which permits them to put in writing easy packages shortly.

There isn’t a doubt that many paperwork can profit from the programming capabilities of . It’s actually pitiful that present tutorials on is uncommon, which considerably limits the event of this language. Hopefully, this text may also help extra individuals get familar with this highly effective device.

Source Link

What's Your Reaction?
Excited
0
Happy
0
In Love
0
Not Sure
0
Silly
0
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top