【问题标题】:LaTeX Immediate EvaluationLaTeX 立即评估
【发布时间】:2010-10-25 16:27:16
【问题描述】:

我正在尝试在 LaTeX 中编写一个命令,该命令采用诸如 8:00A 之类的字符串并将其转换为分钟数,作为使用 TikZ 绘制课程表的脚本的一部分。但是,我遇到了一些问题 - LaTeX 似乎实际上并没有评估命令的内容。

我的命令目前是:

\newcommand{\timetominutes}[1]{
  \IfSubStr{#1}{P}{720}{0}+\IfSubStr{#1}{P}{\StrBetween{#1}{:}{P}}{\StrBetween{#1}{:}{A}}+60*\StrBefore{#1}{:}
}

如果您从中打印出文本,它将正确计算从午夜开始的分钟数。但是,如果在另一个函数中使用,很明显它实际上不会运行任何这些命令——它只是返回包含这些命令的文本。所以如果我写:

\myfunc{\timetominutes{8:00A}}

它看到的是\IfSubStr{8:00A}{P}{720}{0}+\IfSubStr{8:00A}{P}{\StrBetween{8:00A}{:}{P}}{\StrBetween{8:00A}{:}{A}}+60*\StrBefore{8:00A}{:},而不是\myfunc 看到像0+00+60*8 这样有用的东西。这对我来说绝对没用,我无法找到一种方法来强制 LaTeX 在主要命令之前执行子命令。我认为有办法做到这一点,但 LaTeX 文档很少,我似乎找不到任何东西。

或者,如果有办法让 LaTeX 停止抱怨太多 }s(当我有正确的数字时),那可能会奏效。

【问题讨论】:

  • 你到底为什么要在 LaTeX 中做这个?您可以用其他语言编写脚本来读取字符串,处理,然后输出到 TikZ。
  • 听起来像是 luatex 的工作。很抱歉,我无法在 Lua 中提供解决方案。我相信其他人可以。
  • 我受到 TikZ 语法可扩展性的启发,并认为我可以创建类似可扩展的东西。事实证明,我错了。
  • @Mica,我仍然对 luatex 感到困惑,我一直在寻找它以在 TeX 中编程,但我认为它仍然比典型的用户交互更深入。在我弄清楚之前,我将继续使用 PerlTeX(请参阅下面的答案),但能够摆脱包装编译器会很好。

标签: latex


【解决方案1】:

很遗憾,没有。正如 xstring 包所述:

这个包的宏不是纯粹可扩展的

(xstring_doc_en.pdf 的第 3.2 节。)

这个“可扩展”的概念,如果你不熟悉的话,在 TeX 中是一个很棘手的话题。简而言之,不可扩展的东西不能作为参数进行评估。任何在某处使用赋值的东西都保证在大多数 TeX 变体中不可扩展,但也存在其他不可扩展的触发器。对于不熟悉 TeX 的“嘴”(TeX 中处理扩展等事情的部分)内部工作原理的人来说,解决这些问题是相当困难的。

提示:如果 LaTeX 代码是由脚本生成的:使用脚本来转换时间表达式,因为在字符串操作方面几乎任何编程语言都比 TeX 更容易使用。 (或者其他任何事情。)


xstring 包确实暗示了一条出路:您可以通过在调用末尾添加[\variable] 将大多数操作的结果存储在一个变量中。这意味着您需要将\timetominutes 重写为逐段构建结果的内容,然后将该结果存储在命令序列中以供以后使用。

用法:

\timetominutesinto\somevar{8:00A} % \somevar contains 48
\expandafter\myfunc\expandafter{\somevar} % calls \myfunc{48}

注意\expandafter 的使用,它告诉TeX 对下一个命令序列之后 进行简单的一级扩展(评估)。如果你没有使用两个\expandafters,你会得到\somevar作为\myfunc的参数,而不是48。

(注意:前面有丑陋的 TeX 代码!)

\makeatletter % allow @ in command names
\def\timetominutesinto#1#2{% 
  % #1 = command to store the result in
  % #2 = the text to parse
  % \ttm@tempa and \ttm@tempb are temporary variables for this macro
  \let\ttm@tempa\empty % make the command empty
  \IfSubStr{#2}{P}{%
    \def\ttm@tempa{720+}% set tempa to "720+"
    \StrBetween{#2}{:}{P}[\ttm@tempb]% store substring between : and P into tempb
    \edef\ttm@tempa{\ttm@tempa \ttm@tempb}% set tempa to tempa + tempb
  }{%
    \def\ttm@tempa{0+}% set tempa to 0+
    \StrBetween{#2}{:}{A}[\ttm@tempb]% store substring between : and A into tempb
    \edef\ttm@tempa{\ttm@tempa \ttm@tempb}% set tempa to tempa + tempb
  }%
  \edef\ttm@tempa{\ttm@tempa+60*}% set tempa to tempa + "+60*"
  \StrBefore{#2}{:}[\ttm@tempb]% store substring before : into tempb
  % now, set #1 to the result of evaluating the formula returned by concatenating
  % tempa and tempb
  \edef#1{\numexpr \ttm@tempa \ttm@tempb}%
}

\def 是对应于 LaTeX 的 \newcommand/\renewcommand 的 TeX 原语。 \edef 表示扩展 \def,它在将结果分配给命令序列之前评估定义。 \numexpr 计算一个简单的数字表达式,例如 x + m + h * 60 由上面的命令创建。

也可以通过使用整数算术立即将结果计算为一个数字,而无需建立公式。但这会使代码与您的原始意图更加遥远。

可以通过 TeX 本身进行这些字符串操作,而无需使用 xstring 包(在这种特殊情况下甚至可以扩展)。但这是相当低级的东西,如果你不是 TeX 向导,就不容易重复。

【讨论】:

  • LaTeX 代码不会由脚本生成,但在这一点上,我认为用 C# 编写解析代码并将其用于生成会更容易。幸运的是,这将是整个 LaTeX 文档中唯一的一项,因此我可以直接从 C# 打印代码并进行编译。不过,我很好奇这种事情是如何工作的——你能推荐任何书籍或网站,它们是 LaTeX 的全面指南,特别是 LaTeX 编程指南?
  • D.E. 的 TeXbook(计算机和排版,A 部分) Knuth 仍然是了解 TeX 内部的最佳资源之一。接下来,The LaTeX Companion 可能是了解 LaTeX 方面的一个很好的资源。请注意,还有很多关于 CTAN 的信息。
【解决方案2】:

我是在 LaTeX 中进行编程交互的 PerlTeX 的忠实粉丝。使用 Perl 定义函数通常比在 TeX 中编程更容易,并且对于更多的编程操作,它可以更强大。 PerlTeX 为您提供了 perl 和 latex 之间的双向通信,这意味着您可以在 LaTeX 源代码中嵌入 Perl 代码的 sn-ps,而不必编写创建整个源文件的脚本。

这是您想要的示例。使用 perltex --latex=pdflatex test.tex 编译,其中文件已保存为 test.tex,您可以省略可选的 --latex=pdflatex 以生成 dvi。

\documentclass{article}
\usepackage{perltex}

\perlnewcommand{\timetominutes}[1]{
  #Argument is [h]h:mm[ ][a/p[m]] ie 8:00am or 4:05 P or 15:30 (24hr fmt)
  my $input = shift;
  my ($hours, $minutes, $AMPM) = $input =~ /(\d+)\:(\d+)\ *(\w*)/;
  if ($AMPM =~ /p/i) {
    $hours += 12;
  } elsif ($AMPM =~ /a/i and $hours == 12) {
    $hours = 0;
  }
  my $numberofminutes = 60*$hours + $minutes;
  return $numberofminutes;
  ## or you could
  # return '\myfunc{' . $numberofminutes . '}';
  ## or simply
  # return "\\myfunc\{$numberofminutes\}"; 
  ## I can't remember if you need to escape the curly braces
}

\begin{document}

It has been \timetominutes{8:00 pm} minutes since midnight.

\end{document}

就扩展而言,如果您仍然对 \myfunc 有问题,无论它是什么,您都可以将它或它的包装函数定义为 perltex 函数或使用提供的备用返回。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-27
    • 2013-03-09
    • 2013-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-28
    • 2012-11-21
    相关资源
    最近更新 更多