宏不起作用:if-then-else 语句的意外行为

宏不起作用:if-then-else 语句的意外行为

我在使用 LaTeX 宏创建词汇表时遇到了问题。其目的是为字母表中每个新字母生成部分,并为以相同字母开头的术语生成子部分。但是,宏只显示第一部分(例如,如果词汇表以 A 开头,则显示字母 A)。

% package
\usepackage{csvsimple}
\usepackage{xstring}
\usepackage{datatool}

% my macro
\newcommand{\createGlossary}[2]{
    % Load data from CSV file using the specified separator
    \DTLsetseparator{#1}
    \DTLloaddb{glossary}{#2}
    % Sort data alphabetically based on the term
    \DTLsort{termine}{glossary}
    % Variables to track the current and previous letters
    \newcommand{\current}{}
    \newcommand{\previous}{}
    % Iterate over the data to create sections and subsections
    \DTLforeach{glossary}{\term=termine,\description=descrizione}{%
        % Extract the first letter of the term
        \StrLeft{\term}{1}[\current]\par
        
        \typeout{Current: \current}
        \typeout{Previous: \previous}
        
        \IfStrEq{\current}{\previous}{
            \subsection*{\term}
            \description
        }{
            \section*{\current}
            \renewcommand{\previous}{\current}
        }\par
    }
}

尽管遵循与下面提供的 Python 代码类似的逻辑,并且逻辑正确运行,但 LaTeX 宏却失败了。

def print_names(name_list):
    previous_initial = ''
    for name in name_list:
        if name[0] != previous_initial:
            print(name[0])
            previous_initial = name[0]
        print(name)

names = ["Anna", "Giacomo", "Giovanni", "Luigi", "Marco", "Mario", "Paolo"]
print_names(names)

示例用法

\createGlossary{,}{glossary.csv}

将 glossary.csv 替换为包含词汇表数据的 CSV 文件的路径,并根据需要调整分隔符。

问题分析

问题似乎源于 \createGlossary 宏的行为就像“previous”和“current”变量始终相等,从而阻止创建字母表第一个字母以外的新部分。这实际上是我的调试输出:

Current: A
Previous: 
Current: A
Previous: A
Current: A
Previous: A
Current: B
Previous: B
Current: B
Previous: B
Current: B
Previous: B
Current: B
Previous: B
Current: B
Previous: B
...

在此处输入图片描述

如果有人对如何调试和修复 \createGlossary 宏有任何见解或建议,我们将非常感谢您的帮助。

答案1

\renewcommand{\previous}{\current}扩展标记后\previous会产生标记\current,因此的总扩展\previous始终与的总扩展相同\current,因此\IfStrEq对于字符串相等的情况,-test 总是会产生分支。

而不是\renewcommand{\previous}{\current}你需要做的\let\previous=\current

除此之外,

  • 有一个环境description,其底层命令是\description。为了不覆盖该命令,请在代码中定义的所有内部辅助宏的名称前加上“my”前缀。对于实际场景,建议使用更好的命名空间前缀。也许覆盖是/将被限制在由 打开的本地范围内\DTLforeach,但此答案的初始版本的作者不确定是否是这种情况。
  • 在标记化之后{,或者}TeX 的读取装置处于状态 M(行的中间)。当读取装置处于状态 M 并遇到行的末尾时,该行的末尾通常/在正常情况下会在标记流中插入一个空格标记,如果 TeX 在处理时当前正在以水平模式排版内容,则这反过来可能会产生可见的水平空间。
    因此,到处插入注释字符%%%以避免出现不需要的空格标记。
  • \section*并且\subsection*通常在标题后开始一个新段落,因此插入标记\par似乎已经过时了。
\newcommand\myprevious{}%
\newcommand\mycurrent{}%
\newcommand{\createGlossary}[2]{%%%
    % Load data from CSV file using the specified separator
    \DTLsetseparator{#1}%%%
    \DTLloaddb{glossary}{#2}%%%
    % Sort data alphabetically based on the term
    \DTLsort{termine}{glossary}%%%
    % Variables to track the current and previous letters
    \gdef\mycurrent{}%%%
    \gdef\myprevious{}%%%
    % Iterate over the data to create sections and subsections
    \DTLforeach{glossary}{\myterm=termine,\mydescription=descrizione}{%
        % Extract the first letter of the term
        \StrLeft{\myterm}{1}[\mycurrent]%%%
        \typeout{Current: \mycurrent}%%%
        \typeout{Previous: \myprevious}%%%
        %\par        
        \IfStrEq{\mycurrent}{\myprevious}{}{%%%
            \section*{\mycurrent}%%%
            \global\let\myprevious=\mycurrent
        }%%%
        \subsection*{\myterm}%%%
        \mydescription
        %\par
    }%%%
}%%%

相关内容