我如何保存宏的完整展开?

我如何保存宏的完整展开?

我有以下定义:

\newcount\tmpcounta

\def\convertletter#1{%
    \tmpcounta `#1\relax
    \ifnum\tmpcounta<`a{%
        \ifnum\tmpcounta<`A{%
            #1%
        }\else{%
            \ifnum\tmpcounta>`Z{%
                #1%
            }\else{%
                \advance\tmpcounta by -`A\relax
                \advance\tmpcounta by 26\relax
                \number\tmpcounta-%
            }\fi
        }\fi
    }\else{%
        \ifnum\tmpcounta>`z{%
            #1%
        }\else{%
            \advance\tmpcounta by -`a\relax
            \number\tmpcounta-%
        }\fi
    }\fi
}

以及以下代码片段

\usepackage{tikz}% for "foreach" and because I ultimately use it to draw stuff

\def\mylist#1{%
    \foreach \myitem in {#1} {%
        (\expandafter\convertletter\myitem)
    }
}

\mylist{a8,f12}

给出预期的输出:输出符合预期。然而,以下

\usepackage{tikz}

\def\mylist#1{%
    \foreach \myitem in {#1} {%
        \edef\myitem{\expandafter\convertletter\myitem}%
        (\myitem)
    }
}

\mylist{a8,f12}

似乎根本没有转换输入项:意外的输出

我最终想进一步处理结果\expandafter\convertletter\myitem。这就是我尝试将其保存在宏中的原因。但似乎我从根本上误解了宏扩展(尤其是\edef)在 TeX 中的工作方式。

有没有办法保存中间结果以便将其用作其他宏的输入?

答案1

TeX 中的条件语法不是使用括号。你应该说

\ifnum\tmpcnta<`a
  do something
\else
  do something else
\fi

无论如何,您的\convertletter宏使用赋值,因此它不能在中使用\edef

这是一个expl3完全可扩展的实现。

\documentclass{article}
\usepackage{tikz}

\ExplSyntaxOn

\NewExpandableDocumentCommand{\convertletter}{m}
 {
  \mdm_convertletter:N #1
 }

\cs_new:Nn \mdm_convertletter:N
 {
  \bool_if:nTF { \int_compare_p:n { `a <= `#1 <= `z } || \int_compare_p:n { `A <= `#1 <= `Z } }
   {
    \int_eval:n { \int_from_alph:n { #1 } - 1 } -
   }
   {
    #1
   }
 }

\ExplSyntaxOff

\newcommand{\mylist}[1]{%
  \foreach \x in {#1} {%
    \edef\myitem{\expandafter\convertletter\x}%
    (\myitem)%
  }
}

\begin{document}

\mylist{a8,f12,111}

\end{document}

在此处输入图片描述

一个“经典”的实现是

\newcommand\convertletter[1]{%
  \ifnum`#1<`a
    \ifnum`#1<`A
      #1%
    \else
      \ifnum`#1>`Z
        #1%
      \else
        \the\numexpr`#1-`A-26\relax-%
      \fi
    \fi
  \else
    \ifnum`#1>`z
      #1%
    \else
      \the\numexpr`#1-`a\relax-%
    \fi
  \fi
}

您来决定哪一个更容易。

我们可以使用“布尔表达式”。如果 的字符代码介于和的字符代码之间,\bool_if:nTF则谓词形式返回 true ;连接词表示“或”。该函数与大小写无关,对于或,返回 1;对于或,返回 2,依此类推。\int_compare_p:n { `a <= `#1 <= `z }#1az||\int_from_alph:naAbB

答案2

您可以\convertletter使用\isinrange辅助宏来定义宏。语法为\isinrange

 \isinrange X(a-z)\iftrue  Yes, X is in a-z\else No, X isn't in a-z\fi

那么\convertleter宏定义就更加简单了:

\def\isinrange #1(#2-#3)\iftrue{%
   \ifnum 1=\numexpr 0%
      \ifnum`#1<\numexpr`#3+1\relax
        \ifnum`#1>\numexpr`#2-1\relax 1\fi\fi \relax
}
\def\convertletter#1{%
   \isinrange#1(a-z)\iftrue \the\numexpr`#1-`a\relax-%
   \else \isinrange#1(A-Z)\iftrue \the\numexpr`#1-`A-26\relax-%
         \else #1%
   \fi\fi
}

使用 TeX 进行编程是一种乐趣。

相关内容