我正在编写一个程序包来协助排版一些特殊的 Pen & Paper Fantasy RPG 文档。
在这个奇幻的设定中,有三种硬币:金币、银币和铜币,每种硬币的兑换率为 10:1。但是,许多小组使用不同的兑换率(100:1)。因为我想让事情尽可能灵活,所以我定义了一个命令\gold
,它只接受铜币的数量并将其转换为适当的定价字符串。
但是,作为额外的花招,我想支持将所有可购买商品的数据写入外部文件(使用\addcontentsline
),然后可以重新读取该文件以生成一个表格,其中包含收集在一个位置的所有定价信息。这是我的解决方案:
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\def\makedatalines#1{
\AtEndDocument{
\clearpage
\if@filesw
\expandafter\newwrite\csname tf@#1.data\endcsname
\immediate\openout\expandafter\csname tf@#1.data\endcsname%
\jobname.#1.data\relax
\fi
}
}
\def\adddataline#1#2{\expandafter\addtocontents{#1.data}{#2}}
\newcommand{\copperpiecesAlias}{CP}
\newcommand{\silverpiecesAlias}{SP}
\newcommand{\goldpiecesAlias}{GP}
\numgdef{\CopperPerSilver}{10}
\numgdef{\SilverPerGold }{10}
\newcommand {\setCopperPerSilver}[1]{\numgdef{\CopperPerSilver}{#1}}
\newcommand {\setSilverPerGold}[1]{\numgdef{\SilverPerGold}{#1}}
\newcommand{\gold}[1]{%
\numdef{\copperpieces}{#1}%
\ifnumless{\copperpieces}{\CopperPerSilver}{%
\numdef{\silverpieces}{0}%
}{%
\numdef{\silverpieces}{(10*\copperpieces/\CopperPerSilver - 5)/10}%
\numdef{\copperpieces}{\copperpieces - (\CopperPerSilver * \silverpieces)}%
}%
\ifnumless{\silverpieces}{\SilverPerGold}{%
\numdef{\goldpieces}{0}%
}{%
\numdef{\goldpieces}{(10*\silverpieces /\SilverPerGold - 5)/10}%
\numdef{\silverpieces}{\silverpieces - (\SilverPerGold * \goldpieces)}%
}%
\ifnumgreater{\goldpieces }{0}{%
\goldpieces\,\goldpiecesAlias%
\ifnumgreater{\silverpieces}{0}{ }{%
\ifnumgreater{\copperpieces}{0}{ }{}%
}%
}{}%
\ifnumgreater{\silverpieces}{0}{%
\silverpieces\,\silverpiecesAlias%
\ifnumgreater{\copperpieces}{0}{ }{}%
}{}%
\ifnumgreater{\copperpieces}{0}{%
\copperpieces\,\copperpiecesAlias%
}{}%
}
\makedatalines{prices}
\newcommand\purchasable[2]{\textit{#1} (#2)\adddataline{prices}{#1: #2}}
\makeatother
\begin{document}
\purchasable{Longsword} {\gold{1000}}
\purchasable{Broadsword} {\gold{500}}
\purchasable{Magic Staff} {\gold{50000}}
\purchasable{Loaf of Bread}{\gold{5}}
\purchasable{Chicken} {\gold{10}-\gold{50}}
\end{document}
转换工作正常 - 但是:外部文件现在包含如下行
Longsword: \numdef {0}{1000}\numdef {0}{0}\numdef {10}{0}10\,GP
Broadsword: \numdef {0}{500}\numdef {0}{0}\numdef {5}{0}5\,GP
Magic Staff: \numdef {0}{50000}\numdef {0}{0}\numdef {500}{0}500\,GP
Loaf of Bread: \numdef {5}{5}\numdef {0}{0}\numdef {0}{0}5\,CP
Chicken: \numdef {0}{10}\numdef {5}{0}\numdef {0}{0}5\,SP-\numdef {0}{50}\numdef {5}{0}\numdef {0}{0}5\,SP
这显然不是我所希望的(请特别注意最后一行有多混乱 - 这些甚至不是正确的数字)。我需要外部文件包含类似以下行
Longsword: 10\,GP
Broadsword: 5\,GP
Magic Staff: 500\,GP
Loaf of Bread: 5\,CP
Chicken: 1\,SP-5\,SP
经过一番研究,我得出结论,问题在于该\gold
命令不是完全可扩展的。有人能帮我写一个完全可扩展的变体吗\gold
?或者能帮我通过其他方式实现我想要的,并启发我编写这个包的探索吗?此外,我将非常感激有一种更紧凑、更优雅的方式来编写 -function \gold
,它对我来说看起来相当臃肿。
答案1
\numdef
在内部,来自的命令电子工具箱其用途\numexpr
是完全可扩展的,因此您可以使用它来代替。
\iquo
我通过创建辅助函数和\irem
函数来计算整数除法的商和余数,从而稍微简化了代码。为了更清晰起见,我还将其分为仅打印金额的\gold
a\gold
和 a 。\printgold
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\def\makedatalines#1{
\AtEndDocument{
\clearpage
\if@filesw
\expandafter\newwrite\csname tf@#1.data\endcsname
\immediate\openout\csname tf@#1.data\endcsname%
\jobname.#1.data\relax
\fi
}
}
\makeatother
\def\adddataline#1#2{\addtocontents{#1.data}{#2}}
\newcommand{\copperpiecesAlias}{CP}
\newcommand{\silverpiecesAlias}{SP}
\newcommand{\goldpiecesAlias} {GP}
\numgdef{\CopperPerSilver}{10}
\numgdef{\SilverPerGold }{10}
\newcommand{\setCopperPerSilver}[1]{\numgdef{\CopperPerSilver}{#1}}
\newcommand{\setSilverPerGold} [1]{\numgdef{\SilverPerGold}{#1}}
\newcommand{\iquo}[2]{% integer quotient of #1 divided by #2
\romannumeral-`\.% for 2-step expansion
\ifnum#1=0
\expandafter 0%
\else
\the\numexpr(#1-\numexpr(#2-\ifodd \numexpr#2\relax 1 \else 0\fi)/2\relax)/(#2)\expandafter\relax
\fi
}
\newcommand{\irem}[2]{% integer remainder of #1 divided by #2
\the\numexpr #1-#2*\iquo{#1}{#2}\relax}
\newcommand{\gold}[1]{% compute GP/SP/CP
\printgold{\iquo{#1}{\CopperPerSilver*\SilverPerGold}}% Gold pieces
{\iquo{\irem{#1}{\CopperPerSilver*\SilverPerGold}}{\CopperPerSilver}}% Silver pieces
{\irem{\irem{#1}{\CopperPerSilver*\SilverPerGold}}{\CopperPerSilver}}% Copper pieces
}
\newcommand{\printgold}[3]{% Prints GP/SP/CP
% #1 = number of gold pieces
% #2 = number of silver pieces
% #3 = number of copper pieces
\ifnum #1 > 0
#1\,\goldpiecesAlias
\fi
\ifnum \numexpr #1*#2 > 0
\ %
\fi
\ifnum #2 > 0
#2\,\silverpiecesAlias
\fi
\ifnum \numexpr #2*#3 > 0
\ %
\else \ifnum \numexpr #1*#3 > 0
\ %
\fi
\fi
\ifnum #3 > 0
#3\,\copperpiecesAlias
\fi
}
\makedatalines{prices}
\newcommand\purchasable[2]{\textit{#1} (#2)\adddataline{prices}{#1: #2}}
\begin{document}
\purchasable{Longsword} {\gold{1000}}
\purchasable{Broadsword} {\gold{500}}
\purchasable{Magic Staff} {\gold{50000}}
\purchasable{Loaf of Bread}{\gold{5}}
\purchasable{Chicken} {\gold{10}-\gold{50}}
\end{document}