An even more flexible derivative macro?
From the description, it seems like what you want is auto-detection of non-numerical input. That means you do not need \derivative*
but can leave the decision to the code. Robustness could be an issue: in the following I've assume we have a single token for each index (that can be solved). As you've coded in 'traditional' LaTeX, I've stuck as far as possible to the same approach:
\documentclass[12pt]{article}
\makeatletter
\newcommand{\ifintegerTF}[1]{%
\ifnum`#1<`0 %
\expandafter\@secondoftwo
\else
\ifnum`#1>`9 %
\expandafter\expandafter\expandafter\@secondoftwo
\else
\expandafter\expandafter\expandafter\@firstoftwo
\fi
\fi
}
\newcommand{\derivative}[2]{%
\begingroup
\toks@{}%
\@temptokena{}%
\@tempcnta\z@
\@tempswatrue
\@for\@tempa:=#2\do{%
\expandafter\derivative@aux@i\@tempa\stop
}%
\frac
{%
d%
\if@tempswa
\ifnum\@tempcnta>\@ne
^{\number\@tempcnta}%
\fi
#1%
\else
#1%
\ifnum\@tempcnta>\z@
\toks@\expandafter\expandafter\expandafter
{\expandafter\the\expandafter\toks@ \expandafter + \the\@tempcnta}%
\fi
^{\expandafter\@gobble\the\toks@}%
\fi
}
{\the\@temptokena}
\endgroup
}
\newcommand{\derivative@aux}{}
\long\def\derivative@aux@i#1#2\stop{%
\@temptokena\expandafter{\the\@temptokena \, d#1}%
\ifx\\#2\\
\advance\@tempcnta\@ne
\else
\def\@tempb{\@gobble}%
\@for\@tempa:=#2\do{%
\expandafter\derivative@aux@ii\@tempa{#1}%
}%
\@temptokena\expandafter\expandafter\expandafter
{\expandafter\the\expandafter\@temptokena\expandafter^\expandafter{\@tempb}}%
\fi
}
\newcommand{\derivative@aux@ii}[2]{%
\ifintegerTF{#1}
{\advance\@tempcnta#1 }%
{%
\toks@\expandafter{\the\toks@ + #1}%
\@tempswafalse
}%
\protected@edef\@tempb{\@tempb + #1}%
}
\begin{document}
\[
\derivative{x}{{y}{2},{z}{3}}
\]
\[
\derivative{x}{{y}{m},{z}{n}}
\]
\[
\derivative{x}{{v}{3},{w}{k,2},y,{z}{m}}
\]
\end{document}
The key idea is to set a flag to indicate that there are non-numerical index values, and use that flag to then determine how to construct the output.
(If there is interest, I think a LaTeX3 version of this will be somewhat more readable: we don't have an integer test there but the various expansion issues would be easier to solve.)
I think that LuaTeX is ideally suited for such tasks. Here is a proof of concept solution in ConTeXt (to translate to LuaLaTeX, you also need to port utilities.parsers.settings_to_array
function; see util-prs.lua
file in the ConTeXt tree for implementation details of this function)
\startluacode
thirddata = thirddata or {}
-- The pairs() iterator in lua does not guarantee the order in which
-- keys are accessed. So, instead of directly using settings_to_list
-- I use a roundabout iterator.
local settings_to_array = utilities.parsers.settings_to_array
local format = string.format
local split = string.split
function thirddata.partialD(settings)
local list = settings_to_array(settings)
local sum = 0
local num = {}
local den = {}
for i = 1, #list do
local s = split(list[i], "=")
local key, value = s[1], s[2]
local n = tonumber(value)
if n ~= nil then
sum = sum + n
else
num[#num + 1] = value
end
den[i] = format("\\partial %s^{%s}", key, value)
end
num[#num + 1] = sum
num = table.concat(num, "+")
den = table.concat(den)
num = format("\\partial^{%s}", num)
context.dfrac( num, den)
end
\stopluacode
\def\partialD%
{\dosingleargument\dopartialD}
\unprotected\def\dopartialD[#1]{\ctxlua{thirddata.partialD(\!!bs#1\!!es)}}
\enablemode[lmmath]
\starttext
\startTEXpage[offset=3mm]
$\partialD[u=m, x=1, y=2, z=n]$
\stopTEXpage
\stoptext
which gives