9

I am defining a macro in LaTeX2e for writing function declarations.

I want that the macro has tree compulsory arguments: the name of the function (v.g f), a depiction of the type of variable it takes (v.g. x), and the space in which the function is defined (v.g. \mathbb R), as well as three optional arguments: the domain (if the domain is not the same as the codomain), the writing of the function applied to its variables, and the asignment/meaning of the function.

For the sake of clarity when using the macro I had decided for the following structure:

\funcdef{functionName}{variables}[domain]{codomain}[notation][definition]

To avoid ambiguity in the last two optional elements, when notation is given but no definition it takes the form:

\funcdef{functionName}{variables}[domain]{codomain}[notation]

And when definition is given but no notation:

\funcdef{functionName}{variables}[domain]{codomain}*[definition]

I came with the following code (both declaration and example), which is working but seems a little too complicated:

\documentclass{article}
\makeatletter
\newcommand\funcdef[2]{\def\func@name{#1}\def\func@var{#2}%
    \begin{array}{r@{\ }c@{\,}c@{\,}l}#1:\func@dom}
\newcommand\func@dom[2][\@empty]{&\ifx#1\@empty#2\else#1\fi%
    &\to&#2\\&\func@var&\mapsto&\func@use}
\newcommand\func@use{\@ifstar\func@use@\func@usei}
\newcommand\func@use@{\func@name(\func@var)\func@def}
\newcommand\func@usei[1][\@empty]{\ifx#1\@empty\func@name(\func@var)%
    \else#1\fi\func@def}
\newcommand\func@def[1][\@empty]{\ifx#1\@empty\relax\else%
    \mathrel{:=}#1\fi\end{array}}
\makeatother
\begin{document}
Simple function: \[
  \funcdef fx{\mathbf R}
\]

Simple function with declaration: \[
  \funcdef fx{\mathbf R}*[x^2]
\]

Function with alternative writing: \[
  \funcdef{\textrm{exp}}x{\mathbf R}[e^x]
\]textrm{exp}

Function with alternative writing and declaration: \[
  \funcdef\exp x{\mathbf R}[e^x][\lim\limits_{n\to\infty}%
    \left(1+\frac xn\right)^n]
\]

Function with different domain and codomain: \[
  \funcdef{\textrm{sqrt}}n[\mathbf N]{\mathbf R}
\]

Function with different domain and codomain, and alternative
writing: \[
  \funcdef{\textrm{sqrt}}n[\mathbf N]{\mathbf R}[\sqrt n]
\]

Function with different domain and codomain, alternative writing
     and declaration: \[
  \funcdef{\textrm{sqrt}}n[\mathbf N]{\mathbf R}[\sqrt n][\exp(\frac12\ln n)]
\]
\end{document}

Compilation result

Is there any other way to define in LaTeX2e these kind of macros that does not require too many supporting macros (5 in this sample) and temporal definitions (2 in this sample)?

How would it work in pain-TeX or LaTeX3? (answers to this will be upvoted but not accepted)

4
  • 6
    Xparse, job done. How ever I do not think this leaves the source particular readable
    – daleif
    Dec 12, 2013 at 19:47
  • @daleif would my declaration be simplified to \DeclareDocumentCommand\funcdef{mmomsoo}{...} ? Dec 12, 2013 at 20:21
  • 3
    Yes. Then use \IfValueTF{#3}{<true code>}{<false code>} to test whether the optional argument #3 has a value, and \IfBooleanTF{#5}{<star there>}{<star absent>} for the star. I agree with daleif that the source is not readable. Perhaps a key--value syntax would be clearer? Dec 12, 2013 at 20:27
  • Followup Question: A macro adding or removing parenthesis automatically. Dec 9, 2016 at 19:54

2 Answers 2

13

I would use a completely different approach, with a key-value syntax. Six arguments, some of them optional, with a * to denote a missing one are error prone.

\documentclass{article}
\usepackage{amsmath,keyval}

\makeatletter
\newcommand{\funcdef@key}[1]{%
  \define@key{funcdef}{#1}{\@namedef{cet@#1}{##1}}%
  \expandafter\let\csname cet@#1\endcsname\@empty
}
\funcdef@key{name}
\funcdef@key{domain}
\funcdef@key{codomain}
\funcdef@key{variable}
\funcdef@key{notation}
\funcdef@key{definition}
\newcommand{\funcdef@check}[1]{%
  \expandafter\ifx\csname cet@#1\endcsname\@empty
    \@latex@error{Missing `#1'}{Provide `#1'}%
  \fi
}

\newcommand{\funcdef}[1]{%
  \begingroup
  \setkeys{funcdef}{#1}%
  \ifx\cet@codomain\@empty\let\cet@codomain\cet@domain\fi
  \funcdef@check{name}%
  \funcdef@check{domain}%
  \funcdef@check{variable}%
  \begin{array}{l@{}r@{}l@{}l}
  \cet@name\colon{} & 
  \cet@domain &
  {}\to \cet@codomain \\
  &
  \cet@variable &
  {}\mapsto
  \ifx\cet@notation\@empty
    \cet@name(\cet@variable)
  \else
    \cet@notation
  \fi
  \ifx\cet@definition\@empty
    \expandafter\@gobble
  \else
    \expandafter\@firstofone
  \fi
  {& {}\mathrel{:}=\cet@definition}
  \\
  \end{array}
  \endgroup
}
\makeatletter

\begin{document}

\noindent
Simple function:
\[
\funcdef{name=f,variable=x,domain=\mathbf{R}}
\]
Simple function with declaration:
\[
\funcdef{
  name=f,
  variable=x,
  domain=\mathbf{R},
  notation=x^2
}
\]
Function with alternative writing:
\[
\funcdef{
  name=\exp,
  variable=x,
  domain=\mathbf{R},
  notation=e^x
}
\]
Function with alternative writing and declaration:
\[
\funcdef{
  name=\exp,
  variable=x,
  domain=\mathbf{R},
  notation=e^x,
  definition=\lim\limits_{n\to\infty}\left(1+\frac xn\right)^n
}
\]
Function with different domain and codomain:
\[
\funcdef{
  name=\operatorname{sqrt},
  variable=n,
  domain=\mathbf{N},
  codomain=\mathbf{R}
}
\]
Function with different domain and codomain, and alternative
writing:
\[
\funcdef{
  name=\operatorname{sqrt},
  variable=n,
  domain=\mathbf{N},
  codomain=\mathbf{R},
  definition=\sqrt{n}
}
\]
Function with different domain and codomain, alternative writing
and declaration:
\[
\funcdef{
  name=\operatorname{sqrt},
  variable=n,
  domain=\mathbf{N},
  codomain=\mathbf{R},
  notation=\sqrt{n},
  definition=\exp\bigl(\frac{1}{2}\ln n\bigr)
}
\]
\end{document}

enter image description here


Here's an implementation in LaTeX3, with also an “inline” version.

\documentclass{article}
\usepackage{amsmath,xparse}

\ExplSyntaxOn
\keys_define:nn { funcdef }
 {
  name       .tl_set:N   = \l_funcdef_name_tl,
  name       .initial:n  = {},
  domain     .tl_set:N   = \l_funcdef_domain_tl,
  domain     .initial:n  = {},
  codomain   .tl_set:N   = \l_funcdef_codomain_tl,
  codomain   .initial:n  = {},
  variable   .tl_set:N   = \l_funcdef_variable_tl,
  variable   .initial:n  = {},
  variables  .tl_set:N   = \l_funcdef_variables_tl,
  variables  .initial:n  = {},
  notation   .tl_set:N   = \l_funcdef_notation_tl,
  notation   .initial:n  = {},
  definition .tl_set:N   = \l_funcdef_definition_tl,
  definition .initial:n  = {},
  inline     .bool_set:N = \l_funcdef_inline_bool,
 }

\msg_new:nnnn { funcdef } { missing }
 {
  Missing~`#1'
 }
 {
  You~have~to~specify~a~value~for~`#1';~%
  I~have~substituted~??~for~it
 }

\NewDocumentCommand{\funcdef}{ m }
 {
  \group_begin:
  \funcdef_print:n { #1 }
  \group_end:
 }

\cs_new_protected:Npn \funcdef_print:n #1
 {
  \keys_set:nn { funcdef } { #1 }
  \funcdef_check:n { name }
  \funcdef_check:n { domain }
  \tl_if_empty:NT \l_funcdef_variables_tl
   {
    \funcdef_check:n { variable }
   }
  \tl_if_empty:NT \l_funcdef_codomain_tl
   {
    \tl_set_eq:NN \l_funcdef_codomain_tl \l_funcdef_domain_tl
   }
  \bool_if:NTF \l_funcdef_inline_bool
   {
    \funcdef_print_inline:
   }
   {
    \funcdef_print_array:
   }
 }

\cs_new_protected:Npn \funcdef_print_array:
 {
  \begin{array}{ l @{} r @{} l }
  % first row
  \l_funcdef_name_tl \colon {}
  & 
  \l_funcdef_domain_tl
  &
  {}\to \l_funcdef_codomain_tl
  \\
  % second row
  &
  \tl_if_empty:NTF \l_funcdef_variable_tl
   {
    (\l_funcdef_variables_tl)
   }
   {
    \l_funcdef_variable_tl
   }
  &
  {}\mapsto
  \tl_if_empty:NTF \l_funcdef_notation_tl
   {
    \l_funcdef_name_tl (
    \tl_if_empty:NTF \l_funcdef_variable_tl
     {
      \l_funcdef_variables_tl
     }
     {
      \l_funcdef_variable_tl
     }
    )
   }
   {
    \l_funcdef_notation_tl
   }
  \tl_if_empty:NF \l_funcdef_definition_tl
    { \mathrel{:}= \l_funcdef_definition_tl }
  \\
  \end{array}
 }

\cs_new:Npn \funcdef_print_inline:
 {
  \l_funcdef_name_tl \colon
  \l_funcdef_domain_tl
  \to \l_funcdef_codomain_tl
  ,\quad
  \tl_if_empty:NTF \l_funcdef_variable_tl
   {
    (\l_funcdef_variables_tl)
   }
   {
    \l_funcdef_variable_tl
   }
  \mapsto
  \tl_if_empty:NTF \l_funcdef_notation_tl
   {
    \l_funcdef_name_tl (
    \tl_if_empty:NTF \l_funcdef_variable_tl
     {
      \l_funcdef_variables_tl
     }
     {
      \l_funcdef_variable_tl
     }
    )
   }
   {
    \l_funcdef_notation_tl
   }
  \tl_if_empty:NF \l_funcdef_definition_tl
    { \mathrel{:}= \l_funcdef_definition_tl }
 }

\cs_new_protected:Npn \funcdef_check:n #1
 {
  \tl_if_empty:cT { l_funcdef_#1_tl }
   {
    \msg_error:nnn { funcdef } { missing } { #1 }
    \tl_set:cn { l_funcdef_#1_tl } { ?? }
   }
 }

\ExplSyntaxOff

\begin{document}

\noindent
Simple function inline:
\[
\funcdef{inline,name=f,variable=x,domain=\mathbf{R}}
\]
Simple function:
\[
\funcdef{name=f,variable=x,domain=\mathbf{R}}
\]
Simple function with declaration:
\[
\funcdef{
  name=f,
  variable=x,
  domain=\mathbf{R},
  definition=x^2
}
\]
Function with alternative writing:
\[
\funcdef{
  name=\exp,
  variable=x,
  domain=\mathbf{R},
  notation=e^x
}
\]
Function with alternative writing and declaration:
\[
\funcdef{
  name=\exp,
  variable=x,
  domain=\mathbf{R},
  notation=e^x,
  definition=\lim\limits_{n\to\infty}\left(1+\frac xn\right)^n
}
\]
Function with different domain and codomain:
\[
\funcdef{
  name=\operatorname{sqrt},
  variable=n,
  domain=\mathbf{N},
  codomain=\mathbf{R}
}
\]
Function with different domain and codomain, and alternative
writing:
\[
\funcdef{
  name=\operatorname{sqrt},
  variable=n,
  domain=\mathbf{N},
  codomain=\mathbf{R},
  definition=\sqrt{n}
}
\]
Function with different domain and codomain, alternative writing
and declaration:
\[
\funcdef{
  name=\operatorname{sqrt},
  variable=n,
  domain=\mathbf{N},
  codomain=\mathbf{R},
  notation=\sqrt{n},
  definition=\exp\bigl(\frac{1}{2}\ln n\bigr)
}
\]
Function of two variables:
\[
\funcdef{
  name=f,
  variables={a,b},
  domain=A\times B,
  codomain=C,
}
\]
\end{document}

enter image description here

7
  • I would also prefer key-value syntax, if it's not too much effort switching to that. In @egreg's result, there seems to be a small deviation in the second alternative, which misses the f(x) := part; it should be easy to fix.
    – nickie
    Dec 12, 2013 at 21:03
  • @nickie Hawk eye! Just replace, in the second display, notation with definition.
    – egreg
    Dec 12, 2013 at 21:09
  • I'm not sure if I like the key-value approach for this particular project. I rather go for typing shortcuts than for readability. But I appreciate the beauty (and the example) of the key-value. A mixed approach such as \funcdef[codomain=\mathbb N,definition=\rfloor{x}\lfloor]{f}{x}{\mathbb R} might be what I'll end up to. Dec 12, 2013 at 21:41
  • @CarlosEugenioThompsonPinzón I see no gain in this. In a couple of months you won't remember the precise syntax of the command. With keys, you can't miss them and the order doesn't matter.
    – egreg
    Dec 12, 2013 at 21:57
  • In a couple of months I will not remember the name of the keys or its exact orthography (sometimes). And having to type name= for something that always be there..., but this is a matter of personal preferences. Dec 12, 2013 at 22:05
7

This seems to be very easy with xparse. The only thing that seems hard to do is to enforce the exact syntax that you want for differentiating the two last optional arguments. With the solution below, the following are allowed (as you want them):

\funcdef{f}{x}{R}
\funcdef{f}{x}{R}[2x]
\funcdef{f}{x}{R}[2x][x+x]
\funcdef{f}{x}{R}*[x+x]

but the following is allowed too:

\funcdef{f}{x}{R}[2x]*[x+x]

and is equivalent to the third one (of the first four). I do not consider this to be much of a problem; if you do, it will not be easy to fix.

\documentclass{article}
\usepackage{xparse}

\DeclareDocumentCommand{\funcdef}{m m o m o s o}{%
  \begin{array}{r@{\ }c@{\,}c@{\,}l}
    #1:
  & \IfNoValueTF{#3}{#4}{#3}
  & \to & #4 \\
  & #2
  & \mapsto
  & \IfNoValueTF{#5}{#1(#2)}{#5}
    \IfNoValueTF{#7}{}{\mathrel{:=}#7}
  \end{array}
}

\begin{document}
Simple function: \[
  \funcdef fx{\mathbf R}
\]

Simple function with declaration: \[
  \funcdef fx{\mathbf R}*[x^2]
\]

Function with alternative writing: \[
  \funcdef{\textrm{exp}}x{\mathbf R}[e^x]
\]

Function with alternative writing and declaration: \[
  \funcdef\exp x{\mathbf R}[e^x][\lim\limits_{n\to\infty}%
    \left(1+\frac xn\right)^n]
\]

Function with different domain and codomain: \[
  \funcdef{\textrm{sqrt}}n[\mathbf N]{\mathbf R}
\]

Function with different domain and codomain, and alternative
writing: \[
  \funcdef{\textrm{sqrt}}n[\mathbf N]{\mathbf R}[\sqrt n]
\]

Function with different domain and codomain, alternative writing
     and declaration: \[
  \funcdef{\textrm{sqrt}}n[\mathbf N]{\mathbf R}[\sqrt n][\exp(\frac12\ln n)]
\]
\end{document}

result

3
  • I'd probably go with soo at the end, but ...
    – Joseph Wright
    Dec 12, 2013 at 20:59
  • @JosephWright, using soo will make *[LAST] bind LAST to the second last argument, instead of the last one. Then you would have to take into account the presence (or no presence) of the star, which I can completely disregard in my answer. I think that the two are equivalent.
    – nickie
    Dec 12, 2013 at 21:08
  • I can actually see one use for the extra starred version: when you want to allow both the standard functional notation and the alternative notation: f(x)=2x:=x+x Dec 12, 2013 at 21:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .