Thursday, March 03, 2005

If at first you don't succeed...

I've been playing around with PerlTeX, which is an ingenious little system to typeset beautiful documents with the power of Perl. I was inspired to use it because I wanted to illustrate various interest rate calculations without having to do a lot of work updating numbers. For instance, how much will $1000 be worth in a year if it earns 5%? And in two years? And in 5? What about if it earns 6% compounded monthly? None of these is hard individually, but it's tedious and error-prone to do the work by hand.

TeX is the premier typesetting system for mathematics, but it is worthless for performing calculations. I spent several hours trying to work out how I might coerce its macro system to print the result of 365/21. PerlTeX lets me write macros in Perl, so I could calculate Future Value (FV) thusly:

\perlnewcommand{\futurevalue}[3]{
  $_[0]*(1 + $_[1])**$_[2];
}

\futurevalue{1000}{.05}{1}
\futurevalue{1000}{.05}{2}

But this doesn't work:

\futurevalue{1000}{.05/12}{12}
because ".05/12" isn't evaluated. Next I tried a very simple command:
\perlnewcommand*{\perleval}[1]{
  eval qq{@_};
}

\futurevalue{1000}{\perleval{.05/12}}{12}
but this turned out to be hopeless since TeX refused to expand \perleval first. (This might be a solvable problem, but I really don't want to know more than I already do about TeX macros.)

I knew I was on the right track, but I had to sleep on it (and take a shower), before I could hit upon the solution. I needed to do even more in Perl and only make TeX do typesetting:

\perlnewcommand{\perlinit}{
  sub FV{
    $_[0]*(1 + $_[1])**$_[2];
  }
}

\perleval{FV(1000, 0.05/12, 12)}

\perlinit is never called directly. It's sole purpose it to load definitions into the Perl namespace when PerlTeX starts up. \perleval does the work of evaluating Perl expressions. This is my original solution turned inside out.

Now I wanted to round to the nearst cent and add a dollar sign:

\perleval{sprintf '\$%.2f', FV(1000, 0.05/12, 12)}
But TeX swallowed everything after the % as a comment. At this point, the answer was clear:
\perlnewcommand{\perlinit}{
  sub dollar_fmt {sprintf '\$%.2f', $_[0]};

  sub FV{
    $_[0]*(1 + $_[1])**$_[2];
  }
}

\perleval{dollar_fmt FV(1000, 0.05/12, 12)}

No comments: