6

Is it possible to make macro behave as if it was written directly?

\documentclass{article}
\usepackage{amsmath}

\newcommand{\veca}{|_{\vec{a}}}

\begin{document}

\[
\frac{f(\vec{x})}{g(\vec{x})}\bigg\veca \qquad \frac{f(\vec{x})}{g(\vec{x})}\bigg|_{\vec{a}}
\]

\end{document}

enter image description here

4
  • better to use a syntax such as \newcommand{\veca}[1][]{#1|_{\vec{a}} then \veca[\bigg] when you want a size (see the paired delimiter commands in mahtools May 3 at 14:48
  • \frac{f(\vec{x})}{g(\vec{x})}\expandafter\bigg\veca works out, so I suppose \bigg triggers some lookahead at the next token not taking into account that that could be expandable. May 3 at 14:53
  • 1
    @UlrichDiez It's not that it doesn't take expandable commands into account, it's that it's quite common to pass macros there which should not be expanded beforehand. (e.g. \expandafter\bigg\vert would break) May 3 at 15:05
  • I understand that you want the size of the vertical bar to automatically adjust to what precedes. You could simply write \left.\frac{f(\vec{x})}{g(\vec{x})}\right\veca. May 5 at 19:33

5 Answers 5

1
+50

The default definition of \Big and friends sets the delimiter as a left delimiter, so having a subscript on it ends up being the first element in the delimited list and therefore does not get placed relative to the delimiter. This can be avoided by defining \Big and friends in the opposite way: Make the delimiter a right delimiter, such that the subscript becomes a subscript of the whole delimited list.

While this fixes the placement, it can have side effects on how the subscript is placed (mostly \nulldelimiterskip is zero and the style is always scriptstyle) but for most usecases this shouldn't matter.

Given that amsmath defines \big based on \bBigg@ it's enough to change the definition of that macro:

\documentclass{article}
\usepackage{amsmath}

\makeatletter
\renewcommand \bBigg@[2]{%
  {\@mathmeasure\z@{\nulldelimiterspace\z@}%
     {\left.\vcenter to#1\big@size{}\right#2}%
   \box\z@}}
\makeatother

\newcommand{\veca}{|_{\vec{a}}}

\begin{document}

\[
\frac{f(\vec{x})}{g(\vec{x})}\bigg\veca \qquad \frac{f(\vec{x})}{g(\vec{x})}\bigg|_{\vec{a}}
\]

\end{document}

enter image description here

1
  • It's so clever. Thank you so much! I wish you had posted this before I spent lots of time doing it with expl3
    – antshar
    May 12 at 13:22
1

Here's an implementation of @DavidCarlisle's suggestion to make veca take an optional argument, the difference being that I suggest setting \biggr as the default value of the optional argument.

enter image description here

\documentclass{article}
\usepackage{amssymb}
\providecommand{\veca}[1][\biggr]{#1\vert_{\vec{a}}}

\begin{document}
\[
\frac{f(\vec{x})}{g(\vec{x})}\veca
\quad
\frac{u(\vec{x})}{v(\vec{x})}\veca[\Bigr]
\quad
h(\vec{z})\veca[]
\]
\end{document}
2
  • I wouldn't start a bounty if this solution satisfied me.
    – antshar
    May 5 at 17:02
  • 2
    @antshar - You're certainly entirely free, and welcome, to edit your posting and clarifty what exactly your typesetting objective is.
    – Mico
    May 5 at 17:43
1

This is more of a comment than an answer, but such constructions work out of the box in ConTeXt LMTX (i.e., with the luametatex engine).

\define\veca{\rvert_{\vec{a}}}

\starttext
\startformula
\frac{f(\vec{x})}{g(\vec{x})}\Bigg\veca \qquad \frac{f(\vec{x})}{g(\vec{x})}\Bigg|_{\vec{a}}
\stopformula

\stoptext

which gives

enter image description here

I am not sure what is different beneath the hood for this to work correctly.

Note that the size of \bigg and \Bigg are different because ConTeXt does scaling of delimiters a bit differently, and the placement of the subscript is different (that is because luatex engine reads the spacing values from the font; you get similar spacing in lualatex as well).

0

Is it possible to make macro behave as if it was written directly?

Since TeX evaluate "left-to-right", if you have \outermacro{\innermacro} and keep outermacro intact (*1), nothing can force it to expand innermacro if it doesn't want to.

(*1): the OP's other question address the case of modifying the outer macro. Works as well.

Unless... \veca is not-quite a macro. Do this to do replacement on the source code before reading each line.

%! TEX program = lualatex
\documentclass{article}
\usepackage{amsmath}
\usepackage{luacode}

\begin{luacode*}
    function translate(line)
        line=string.gsub(line, [[\veca([^A-Za-z])]], [[|_{\vec{a}}%1]])
        line=string.gsub(line, [[\veca$]], [[|_{\vec{a}}]])
        return line
    end
    luatexbase.add_to_callback("process_input_buffer", translate, "veca special replacement")
\end{luacode*}


\begin{document}

\[
\frac{f(\vec{x})}{g(\vec{x})}\bigg\veca \qquad \frac{f(\vec{x})}{g(\vec{x})}\bigg|_{\vec{a}}
\]


% test:
\def\vecab{\vec{ab}}
\[
\vecab
% ^^ middle of a word, don't replace
+
x\veca
% ^^ end of line, still replace
\]

\end{document}

Of course, this way it doesn't behave the same way a macro does, e.g. if it appear in \verb it will still be replaced with the result. And it's up to you to write the "parser" correctly.

2
  • I'm not responsible if you forget about this replacement thing and wonder why you get mysterious errors when the code totally looks correctly.
    – user202729
    May 9 at 6:55
  • Side note, things other than LuaTeX can do this replacement too, but there are some... drawbacks.
    – user202729
    May 9 at 6:56
0

enter image description here

\documentclass{article}
\usepackage{amsmath}
\usepackage{expl3}

\makeatletter
\ExplSyntaxOn

\cs_set_eq:NN \better_big:nn \bBigg@

\cs_set:Npn \bBigg@ #1#2 {
  \tl_set:Nx \arg_rest_tokens { \tl_tail:N {#2} }
  \tl_set:Nx \arg_first_token { \tl_head:N {#2} }
  \tl_set:Nx \arg_first_token_exp { \tl_head:f {#2} }
  \exp_last_unbraced:No \token_if_eq_meaning:NNT \arg_first_token \delimiter {
    \use_none_delimit_by_q_nil:w
  }
  \exp_last_unbraced:No \token_if_eq_meaning:NNF \arg_first_token_exp \delimiter {
    \exp_last_unbraced:Nno \str_if_in:nnF {\{\}} {\arg_first_token} {
      \int_compare:nF { \exp_last_unbraced:NNV \delcode`\arg_first_token > 0 } {
        \use_none_delimit_by_q_nil:w
      }
    }
  }
  \better_big:nn {#1} {\arg_first_token} \arg_rest_tokens
  \use_none_delimit_by_q_stop:w
  \use_none_delimit_by_q_nil:w \q_nil
  \better_big:nn {#1}{#2}
  \use_none_delimit_by_q_stop:w \q_stop
}

\ExplSyntaxOff
\makeatother

\newcommand{\veca}{|_{\vec{a}}}
\newcommand{\ketb}{\rangle^*}
\newcommand{\lnorm}{\|}
\newcommand{\rnorm}{\|_\infty}


\begin{document}

Custom macros next to \verb|\Big, \bigg|, etc
\[
\frac{f(\vec{x})}{g(\vec{x})}\bigg\veca \qquad
\big\lnorm A\vec{x} \big\rnorm \qquad
\Big|\Phi(t)\Big\ketb
\]

Doesn't break the regular behaviour
\[
\frac{f(\vec{x})}{g(\vec{x})}\bigg|_{\vec{a}} \qquad
\big\| A\vec{x} \big\|_\infty \qquad
\Big|\Phi(t)\Big\rangle^*
\]

\end{document}

The question itself seems pretty simple, doesn't it. However, it appeared way harder to achieve that I firstly though, to be honest.

It took me lots of effort to finally solve the problem. I have to thank everyone who contributed to this and helped me to solve adjacent questions.

Special thanks to egreg that come up with the way to detect delimiters in general.


Explanation

All \big, \bigg, \Big, \Bigg are defined in amsmath like so

\renewcommand{\big}{\bBigg@\@ne}
\renewcommand{\Big}{\bBigg@{1.5}}
\renewcommand{\bigg}{\bBigg@\tw@}
\renewcommand{\Bigg}{\bBigg@{2.5}}
\ifx\leavevmode@ifvmode\@undefined
\def\bBigg@#1#2{%
  {\@mathmeasure\z@{\nulldelimiterspace\z@}%
     {\left#2\vcenter to#1\big@size{}\right.}%
   \box\z@}}
\else
\def\bBigg@#1#2{\leavevmode@ifvmode
  {\@mathmeasure\z@{\nulldelimiterspace\z@}%
     {\left#2\vcenter to#1\big@size{}\right.}%
   \box\z@}}
\fi

so the changes need to apply only to \bBigg@

First what comes to mind is to simply expand the argument of \bigs commands. It works at the first sight, but actually it breaks some of cases, for example \big\vert would led to an error.

Therefore, the idea that eventually implemented is the following: Check whether the argument is expandable and if so, check the first argument of the expansion, i.e \veca's expansion is {|_\vec{a}} and then I check the first token of that list and if it's a delimiter then I pass only it to the \big command and everything else just appends rightwards.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.