可扩展单位换算

可扩展单位换算

我正在编写一个程序包来协助排版一些特殊的 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函数来计算整数除法的商和余数,从而稍微简化了代码。为了更清晰起见,我还将其分为仅打印金额的\golda\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}

相关内容