1

I am trying to create a command \projlib_math_define_shortcut:nnn for defining shortcuts for math letters. Ideally, it should be able to use like \projlib_math_define_shortcut:nnn { mathcal } { cal, mc } { A, B, C }, which should do:

  1. create two short cuts \cal and \mc for \mathcal
  2. create shortcuts for the letters given, in this case, \calA and \mcA for \mathcal{A}, etc.

For this, I make use of the clist structure of expl3. I think it is my wrong usage of ## and ### that causes the errors (Illegal parameter number in definition of \__clist_map_1:w.). My current code is listed in the following MWE. Could you please tell me why does it produce errors and how should it be fixed?

\documentclass{article}

\usepackage{amssymb}

\ExplSyntaxOn

% \projlib_math_define_shortcut:nnn { math command } { shortcut name(s) } { shortcut member(s) } \cs_new:Nn \projlib_math_define_shortcut:nnn { \clist_set:Nn \l_tmpa_clist { #2 } \clist_set:Nn \l_tmpb_clist { #3 } \clist_map_inline:Nn \l_tmpa_clist { % create \shortcut{} \tl_if_blank:eF { ##1 } { \tl_if_exist:cF { ##1 } { \tl_gset:cn { ##1 } { \ensuremath { \tl_use:c { #1 } { ###1 } } } } } % create \shortcut \clist_map_inline:Nn \l_tmpb_clist { \tl_if_exist:cF { ##1 ###1 } { \tl_gset:cn { ##1 ###1 } { \ensuremath { \tl_use:c { #1 } { ###1 } } } } } } }

\projlib_math_define_shortcut:nnn { mathbb } { bb } { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z }

\projlib_math_define_shortcut:nnn { mathfrak } { mf, frak } { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z }

\projlib_math_define_shortcut:nnn { mathcal } { mc, cal } { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z }

% \projlib_math_define_shortcut:nnn { mathbb } { } % { % N, Z, Q, R, C, F, A % }

\ExplSyntaxOff

\begin{document}

\fraka

\bbA

% \C

\end{document}

Jinwen
  • 8,518
  • Three # is wrong. You'll have to double the number of # for each level of nesting. So # for direct parameter, ## for parameter inside of a definition, #### for a parameter inside the definition of a definition of a definition, etc. – Skillmon Feb 05 '22 at 12:54
  • @Skillmon Thank you! I thought that each time only one more # shall be needed. – Jinwen Feb 05 '22 at 13:00
  • it's just that ## produces # just as #1 produces the first argument, the fact that it doubles each time is a consequence of that: there is no special code for more than two #, just the outer definition you need ## for each # so if you need ## for the inner definition then you need #### in the outer one. – David Carlisle Feb 05 '22 at 14:03

3 Answers3

1

I'm not sure why defining several different shortcuts for the same commands, as they will be confusing. Also, you should not provide the shortcuts in the package, as users might have different ideas about what \mfb, say, might mean and they wouldn't be able to use it for obscure reasons (that the package already provides it). So define a user level command for that purpose and document its usage.

In any case I'd leave out \ensuremath: you gain nothing by allowing \calA{} in text mode instead of the clearer and more semantic $\calA$.

You also want to avoid TeX defining so many token list variables and do \tl_use:c { cal } { A } which is also disputable from a conceptual point of view.

Let's see: you want, given \macro that takes one argument, to define a shortcut prefix, say pre, and, for each letter in the list, say X, you want \preX to stand for \macro{X}.

So what you want is something like

\cs_new_protected:Npn \preX { \macro{X} }

which can be accomplished with

\cs_new_protected:cpn { preX } { \macro{X} }

So here's the code: instead of doing everything in the same function, I split the mapping into two parts.

\documentclass{article}
\usepackage{amsmath,amssymb}

\ExplSyntaxOn

\cs_new_protected:Nn \projlib_math_define_shortcut:Nnn {% #1 is the math command % #2 is a list of prefixes % #3 is a list of letters \clist_map_inline:nn { #2 } { __projlib_math_define_shortcut_do:Nnn #1 { ##1 } { #3 } } }

\cs_new_protected:Nn __projlib_math_define_shortcut_do:Nnn {% #1 is the math command % #2 is a prefix % #3 is a list of letters \clist_map_inline:nn { #3 } { \cs_new_protected:cpn { #2##1 } { #1{##1} } } }

\projlib_math_define_shortcut:Nnn \mathbb { bb } { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, % a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z }

\projlib_math_define_shortcut:Nnn \mathfrak { mf, frak } { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z }

\ExplSyntaxOff

\begin{document}

$\bbA+\bbZ+\mfA+\mfb+\frakY$

\end{document}

enter image description here

If you prefer to use {mathfrak} instead of \mathfrak (but I can't see why), you can use \exp_not:c.

\documentclass{article}
\usepackage{amsmath,amssymb}

\ExplSyntaxOn

\cs_new_protected:Nn \projlib_math_define_shortcut:nnn {% #1 is the math command name % #2 is a list of prefixes % #3 is a list of letters \clist_map_inline:nn { #2 } { __projlib_math_define_shortcut_do:nnn { #1 } { ##1 } { #3 } } }

\cs_new_protected:Nn __projlib_math_define_shortcut_do:nnn {% #1 is the math command name % #2 is a prefix % #3 is a list of letters \clist_map_inline:nn { #3 } { \cs_new_protected:cpx { #2##1 } { \exp_not:c { #1 }{##1} } } }

\projlib_math_define_shortcut:nnn {mathbb} { bb } { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, % a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z }

\projlib_math_define_shortcut:nnn {mathfrak} { mf, frak } { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z }

\ExplSyntaxOff

\begin{document}

$\bbA+\bbZ+\mfA+\mfb+\frakY$

\end{document}

Some other points to note.

  1. If you nest a mapping inside another, you need to double the number of # characters, not add one.

  2. Doing \clist_set:Nn \l_tmpa_clist { #2 } is a waste, because you can use \clist_map_inline:nn { #2 } { ... }.

  3. There's no need to check whether the current item during the mapping is empty, because \clist_map_inline:nn will ignore empty items anyway.

egreg
  • 1,121,712
  • Thank you for your detailed explanation! In my old LaTeX2e version package, those short cuts are defined using \providecommand, so it won't add extra trouble for user-defined macros. However I was not able to find an expl3-equavalent of \providecommand that allows to use :c specifier. In an answer below I posted the current version which contains a few \cs_if_exist:cF check, but it is still not satisfying as when user define those existed commands, errors would arise. – Jinwen Feb 05 '22 at 14:39
1

If you don't use expl3, macros have more compact form:

\def\mydefmath #1#2 #3{\expandafter\def\csname #2#3\endcsname{#1{#3}}}
\def\Letters{ABCDEFGHIJKLMNOPQRSTUVWXYZ}
\def\letters{abcdefghijklmnopqrstuvwxyz}
\def\mydefall #1#2 #3{\def\tmp{\mydefmath #1#2 }\expandafter\mydefallA#3{}}
\def\mydefallA #1{\ifx^#1^\else \tmp#1\expandafter\mydefallA\fi}

\mydefall \mathbb bb \Letters \mydefall \mathfrak mf \Letters \mydefall \mathfrak mf \letters \mydefall \mathfrak frak \Letters \mydefall \mathfrak frak \letters \mydefall \mathcal mc \Letters \mydefall \mathcal cal \Letters \mydefall \mathscr ms \Letters \mydefall \mathscr scr \Letters

%% testing:

\meaning\msC % -> \mathscr{C} \meaning\bbD % -> \mathbb{D} \meaning\mfa % -> \mathfrak{a}

\end

wipet
  • 74,238
0

Following @egreg's great answer above, here is the completed version:

\documentclass{article}
\usepackage{amsmath,amssymb,mathpazo}

\ExplSyntaxOn

\cs_new_protected:Nn \projlib_math_define_shortcut:Nnn % #1 is the math command % #2 is a prefix % #3 is a list of letters { \tl_if_blank:eTF { #2 } { % create \shortcut* \clist_map_inline:nn { #3 } { \cs_if_exist:cF { ##1 } { \cs_new_protected:cpn { ##1 } { #1 {##1} } } } } { \clist_map_inline:nn { #2 } { % create \shortcut{} and \shortcut* __projlib_math_define_shortcut_do:Nnn #1 { ##1 } { #3 } } } }

\cs_new_protected:Nn __projlib_math_define_shortcut_do:Nnn { % create \shortcut{} \cs_if_exist:cF { #2 } { \cs_new_protected:cpn { #2 } { #1 } } % create \shortcut* \clist_map_inline:nn { #3 } { \cs_if_exist:cF { #2 ##1 } { \cs_new_protected:cpn { #2 ##1 } { #1{##1} } } } }

\projlib_math_define_shortcut:Nnn \mathbb { bb } { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 1 }

\projlib_math_define_shortcut:Nnn \mathbb { } { N, Z, Q, R, C, F, A }

\projlib_math_define_shortcut:Nnn \mathfrak { mf, frak } { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z }

\projlib_math_define_shortcut:Nnn \mathcal { mc, cal } { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z }

\projlib_math_define_shortcut:Nnn \mathscr { ms, scr } { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z }

\ExplSyntaxOff

\begin{document}

$\bbA+\bbZ+\mfA+\mfb+\frakY$

$\C \N \Q \R \F \bb1$

\end{document}

enter image description here

Jinwen
  • 8,518
  • I would never use such a system. You reserve lots of good short command names for not much gain (and a high probability to clash with the commands of some other package). – Ulrike Fischer Feb 05 '22 at 16:46
  • @UlrikeFischer Indeed, this is just for writing draft notes quicker when code snippets are not available. Since in the final package I define all of these shortcuts with \ProvideDocumentCommand and delay them to the end of the preamble, it should usually cause no conflict. – Jinwen Feb 05 '22 at 16:52
  • There is always a conflict if two packages try to use the same command name as only one package can win. You can avoid error messages with \providecommand and tests, but you can't avoid to confuse the user when e.g. \calc or \R gives some different output depending on the packages in the preamble. – Ulrike Fischer Feb 05 '22 at 16:57
  • @UlrikeFischer Yes, I am aware of that, and that is probably why I chose to have multiple shortcuts for each symbol -- there always will be one available. There is another program to change all such shortcuts into its full version to produce the final document, and the goal of these shortcuts are to be convenient during draft phase and not to raise any error if the user does not use them (since their definition is placed at the end of preamble, usually the definition from the other package should win in case of name conflict). When editors have code snippets, I won't even use them myself. – Jinwen Feb 05 '22 at 17:04
  • 1
    Just consider that there is another package which uses the same methods as yours. It also defines lots of commands and does it "at the end of preamble". How on earth should a user know which definition actually wins without lots of testing? And why your package doesn't do what they expect from it? – Ulrike Fischer Feb 05 '22 at 17:25
  • @UlrikeFischer Ah, I think you are right. Actually I thought that those widely used packages shouldn't contain such silly part as mine does, so I assumed that such conflicts shouldn't occur in practice. And since this is really meant for writing draft, if there are symbolic mistakes then one should be able to see it by eyes, and choose not to use the conflict ones in the current document. And for the worst situation, there is a package option for completely disable those shortcuts. Based on last year's note-taking experience, I'd say things works reasonably fine. – Jinwen Feb 05 '22 at 17:35