Text FormattingMacro Fail

Information and discussion about LaTeX's general text formatting features (e.g. bold, italic, enumerations, ...)
LaTexLearner
Posts: 139
Joined: Tue Mar 10, 2015 11:06 am

Re: Macro Fail

Post by LaTexLearner »

Got it.

Thanks!

Recommended reading 2024:

LaTeXguide.org • LaTeX-Cookbook.net • TikZ.org

NEW: TikZ book now 40% off at Amazon.com for a short time.

LaTexLearner
Posts: 139
Joined: Tue Mar 10, 2015 11:06 am

Macro Fail

Post by LaTexLearner »

Johannes_B wrote:Underlining some space works nicely, but seems a bit strange. How about using the simple and built-in rule?

Code: Select all

\documentclass{article}
\newlength{\answerlinethickness}
\setlength{\answerlinethickness}{.4pt}
\newlength{\defaultlinelength}
\setlength{\defaultlinelength}{1.5em}
\newcommand{\answerline}[1][\defaultlinelength]{\rule{#1}{\answerlinethickness}}
\begin{document}
And the asnwer is \answerline[2cm].

\begin{displaymath}
\begin{array}{r@{= \answerline}}
	3+4 \\
	7+1 \\ 
	47 + 7 \\
\end{array}
\end{displaymath}
\setlength{\answerlinethickness}{1pt}
And the asnwer is \answerline[2cm].
\end{document}
Here we use an optional argument to set the length of the line. The default value was set to 1.5em; 150% the width of the letter M.

Say I wanted two optional arguments each with their own default. This is how I figured it would be done, but it is not working.

Why not?

And how would I adjust only one of the two defaults?

Code: Select all

\documentclass{article}

\begin{document}

\newlength{\ALT} % ALT=AnswerLineThickness
\setlength{\ALT}{1pt}

\newlength{\DLL} % DLL=DefaultLineLength.
\setlength{\DLL}{1.5cm}

\newcommand{\answerspace}[1][\DLL][2][\ALT]{\rule{#1}{#2}} 

\answerspace

\answerspace[5cm][1cm]

\end{document}
rais
Posts: 419
Joined: Sun Nov 16, 2014 8:51 pm

Macro Fail

Post by rais »

LaTexLearner wrote: Say I wanted two optional arguments each with their own default. This is how I figured it would be done, but it is not working.

Why not?
because \newcommand doesn't provide the means for declaring a macro with two optional arguments, even though \newcommand itself knows two optional arguments (the number of arguments the to-be-defined macro is to understand and the default value for the first argument---and thus declaring the first argument as optional).
LaTexLearner wrote: And how would I adjust only one of the two defaults?
The, hmm, classical approach would be to define a command with only one optional argument and check for a following opening bracket (indicating a second optional argument following) in its body, then call a helper macro that expects two arguments in brackets each:

Code: Select all

\documentclass{article}

\newlength{\ALT} % ALT=AnswerLineThickness
\setlength{\ALT}{1pt}

\newlength{\DLL} % DLL=DefaultLineLength.
\setlength{\DLL}{1.5cm}
\makeatletter% let @ be a letter
\newcommand*\answer@space@ll{}% just for the case the first argument might be empty, say, one wants to call \answerspace with default line length and only change the line thickness
\def\answerspace@intern[#1][#2]{% here, the brackets are mandatory; that's why this command shouldn't be used directly---hence the @ character in its name: from within a LaTeX document body, LaTeX will see `\answer' as a command name, followed by @...
  \rule{#1}{#2}%
}
\newcommand*\answerspace[1][\DLL]{%
  \ifx\relax#1\relax % if argument for line length is empty
    \edef\answer@space@ll{\DLL}% back to default
  \else
    \edef\answer@space@ll{#1}% else assume valid argument
  \fi
  \@ifnextchar[% if second opening bracket present
  {\answerspace@intern[\answer@space@ll]}% \answer@space@intern can collect the second argument
  {\answerspace@intern[\answer@space@ll][\ALT]}% else the second argument's default goes here
} 
\makeatother% let @ be something else (again)
\begin{document}

\answerspace % no argument, assuming defaults for line length and line thickness both

\answerspace[5cm][1cm]% two arguments, defining line length and line thickness both

\answerspace[1pc]% one argument, defining line length only and using default for line thickness

\answerspace[][1pc]% two arguments, with the first one empty: using default line length and defining line thickness

\end{document}
Alternatively, you could use the twoopt package:

Code: Select all

\documentclass{article}
\usepackage{twoopt}

\newlength{\ALT} % ALT=AnswerLineThickness
\setlength{\ALT}{1pt}

\newlength{\DLL} % DLL=DefaultLineLength.
\setlength{\DLL}{1.5cm}

\newcommandtwoopt{\answerspace}[2][\DLL][\ALT]{%
  \ifx\relax#1\relax
    \rule{\DLL}{#2}%
  \else
    \rule{#1}{#2}%
  \fi
}

\begin{document}

\answerspace

\answerspace[5cm][1cm]

\answerspace[1pc]

\answerspace[][1pc]
\end{document}
or, if you want to be more flexible, use xparse:

Code: Select all

\documentclass{article}
\usepackage{xparse}

\newlength{\ALT} % ALT=AnswerLineThickness
\setlength{\ALT}{1pt}

\newlength{\DLL} % DLL=DefaultLineLength.
\setlength{\DLL}{1.5cm}

\NewDocumentCommand{\answerspace}{O{\DLL}O{\ALT}}{%
  \ifx\relax#1\relax
    \rule{\DLL}{#2}%
  \else
    \rule{#1}{#2}%
  \fi
}

\begin{document}

\answerspace

\answerspace[5cm][1cm]

\answerspace[1pc]

\answerspace[][1pc]
\end{document}
As for the question about a single argument, you basically put the one argument where you think it needs changing the most as first argument, since a single optional argument will be interpreted as the first one (and for the missing second one, its default is used).
For calling the macro with only the second optional argument, you could leave the first one empty, but then you must catch this case in your macro (in the examples above done with \ifx\relax#1\relax, where the #1 gets expanded, and if nothing's in this first parameter, \ifx compares \relax with \relax, which will be true.


KR
Rainer
LaTexLearner
Posts: 139
Joined: Tue Mar 10, 2015 11:06 am

Re: Macro Fail

Post by LaTexLearner »

Wow, thanks for that amazing response!

It will now take me 18 days to digest it. :)
User avatar
Johannes_B
Site Moderator
Posts: 4182
Joined: Thu Nov 01, 2012 4:08 pm

Macro Fail

Post by Johannes_B »

The test for an empty optional argument with package etoolbox instead of low level TeX.

Code: Select all

\documentclass{article}
\usepackage{xparse}
\usepackage{etoolbox}

\newlength{\ALT} % ALT=AnswerLineThickness
\setlength{\ALT}{1pt}

\newlength{\DLL} % DLL=DefaultLineLength.
\setlength{\DLL}{1.5cm}

\NewDocumentCommand{\answerspace}{O{\DLL}O{\ALT}}{%
	\ifblank{#1}{%
		\rule{\DLL}{#2}%
	}{%
		\rule{#1}{#2}%
	}
}

\usepackage{parskip}%for the example
\begin{document}

\answerspace

\answerspace[5cm][1cm]

\answerspace[1pc]

\answerspace[][1pc]
\end{document}
The smart way: Calm down and take a deep breath, read posts and provided links attentively, try to understand and ask if necessary.
Post Reply