How to command LaTeX to treat a non-value as 0

Just put a leading zero so the number of loops is 0#1. When #1 is empty you have zero, otherwise you have #1.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \lines { m }
  {
    \int_step_inline:nn { 0#1 }
      { \vskip 5mm~\noindent \makebox[\linewidth]{\dotfill} }
    \vskip 8mm~\setlength{\rightmargin}{5em}%
  }
\ExplSyntaxOff
\begin{document}
a\lines{}

b\lines{3}
\end{document}

Here's a very slightly more elaborate version that accepts \count registers and a few more edge cases (definitely not fool-proof)?

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \lines { m }
  {
    \int_step_inline:nn { \tl_if_blank:eT {#1} { 0 } #1 }
      { \vskip 5mm~\noindent \makebox[\linewidth]{\dotfill} }
    \vskip 8mm~\setlength{\rightmargin}{5em}%
  }
\ExplSyntaxOff
\begin{document}
a\lines{}

b\lines{3}

c\lines{ 3 }

\count178=3
d\lines{\count178}

\newcount\x\x=3
e\lines{\x}

\chardef\x=3
f\lines{\x}

g\lines{\numexpr 1+1+1}

h\lines{\dimexpr 0.00005pt}

\def\empty{}
i\lines{\empty}

\def\empty{}
j\lines{ \empty}

% Still fails in this case, but then you're asking for it ;-)
\protected\def\empty{}
% k\lines{\empty}

\end{document}

I would do this slightly differently and make the argument to \lines an optional argument, which defaults to zero. This way \lines prints zero dotted lines and \lines[3] prints lines etc.

Here's the modified code:

\documentclass{article}
\usepackage{multido}

\newcommand{\lines}[1][0]{%
  \multido{}{#1}{\vskip 5mm \makebox[\linewidth]{\dotfill}}%
  \vskip 8mm\setlength{\rightmargin}{5em}%
}

\begin{document}

  \lines

  \lines[3]

  \lines[6]

\end{document}

Btw, rather than posting code snippets it is always better to post a full minimal working example as this makes it easier for people to play with and modify your code.

Edit If in your use-case you need \line{} to be the same as \line{0} then you can use \detokenize:

\documentclass{article}
\usepackage{multido}
\usepackage{xparse}

\newcommand{\lines}[1]{%
\multido{}{\if\relax\detokenize{#1}\relax0\else#1\fi}{\vskip 5mm \makebox[\linewidth]{\dotfill}}%
  \vskip 8mm\setlength{\rightmargin}{5em}%
}

\begin{document}

  \lines{}

  \lines{3}

  \lines{6}

\end{document}

The \detokenize{#1} "neutralises" all of the category codes that appear in #1 so that no surprises can arise. After the the "content" of #1 is compared with \relax. If #1 is empty then \detokenize{#1} is nothing so the \if statement expands to \if\relax\relax0\else#1\fi. In other words, if #1 is blank then \if-statement expands to 0 and otherwise it expands to #1.


Usage of the \multido-command is:

\multido{⟨variables⟩}{⟨repetitions⟩}{⟨stuff⟩}

Seems ⟨repetitions⟩ can by anything that yields a TeX-⟨number⟩-quantity.

If ⟨repetitions⟩ is negative, ⟨variables⟩ will be counted backwards.

Thus with your code-example \lines{3} and \lines{-3} yield the same.

If ⟨repetitions⟩ is provided in terms of something that, e.g., denotes a \count-register or a \countdef-token, just prepending 0 to the ⟨repetitions⟩-argument causes some ! Missing number, treated as zero.-error. If ⟨repetitions⟩ is provided in terms of an alphabetic constant , just prepending 0 to the ⟨repetitions⟩-argument also does not lead to the desired result.

Therefore I suggest something like applying the \multido-thing only in case both

\romannumeral\number\number⟨number⟩␣0

and

\romannumeral-\number\number⟨number⟩␣0

yields emptiness.

(\romannumeral does silently not return any token in case the number to convert is not positive. So let's multiply both the number to convert and the negative number to convert by 10 by appending 0 via \number\number⟨number⟩␣0-trickery for ensuring that the thing works out with things like \count20 (whereafter a space is needed for separating the 20, which is the number of the \count-register) also. In case the argument holding the ⟨number⟩ consists of something that yields ⟨optional signs⟩ only—the case of the argument holding the ⟨number⟩ being empty or blank/yielding emptiness or blankness falls into this category—, the appendend 0 will turn the expression into a valid ⟨number⟩-quantity whose value is 0.)

\documentclass[a4paper]{article}

% Vertically adjust the page layout so that the example creates exactly one page:
\csname@ifundefined\endcsname{pdfpagewidth}{}{\pdfpagewidth=\paperwidth\relax}%
\csname@ifundefined\endcsname{pdfpageheight}{}{\pdfpageheight=\paperheight\relax}%
\csname@ifundefined\endcsname{pagewidth}{}{\pagewidth=\paperwidth\relax}%
\csname@ifundefined\endcsname{pageheight}{}{\pageheight=\paperheight\relax}%
\pagestyle{plain}%
\headheight=0ex\relax
\headsep=0ex\relax
\topmargin=1cm\relax
\footskip=.5\topmargin\relax
\begingroup\normalfont
\expandafter\endgroup\expandafter\advance\expandafter
\footskip\the\dimexpr.5\ht\strutbox\relax\relax
\textheight=\paperheight\relax
\advance\textheight-2\topmargin\relax
\advance\topmargin-1in\relax
%%%%%%%%%%%%% Page layout adjustments done. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\usepackage{multido}

\makeatletter
\newcommand\exchange[2]{#2#1}%
\newcommand{\lines}[1]{%
  \if Y\expandafter\exchange\expandafter{\romannumeral\number\number#1 0}%
       {\expandafter\@firstofone\expandafter{\romannumeral-\number\number#1 0}}Y%
    \expandafter\@gobble\else\expandafter\@firstofone\fi
  {\multido{}{#1}{\vskip 5mm \makebox[\linewidth]{\dotfill}}}%
  \vskip8mm \setlength{\rightmargin}{5em}%
}%
\makeatother


\begin{document}

\verb*|\somecount=3|:

\verb*|\lines{\somecount}|:

\newcount\somecount
\somecount=3

\lines{\somecount}

\hrule\kern\dp\strutbox

\verb*|\lines{\the\somecount}|:

\lines{\the\somecount}

{
\hrule\kern\dp\strutbox

\verb*|\count90=3|

\verb*|\lines{\count90}|:

\count90=3 
\lines{\count90}

\hrule\kern\dp\strutbox

\verb*|\lines{\count90 }|:

\lines{\count90 }

}

\hrule\kern\dp\strutbox

\verb*|\lines{3}|:

\lines{3}

\hrule\kern\dp\strutbox

\verb*|\lines{-000003}|:

\lines{-000003}

\hrule\kern\dp\strutbox

\verb*|\lines{}|:

\lines{}

\hrule\kern\dp\strutbox

\verb*|\lines{0}|:

\lines{0}

\hrule\kern\dp\strutbox

\end{document}

enter image description here

But there are still problems/issues:

  • Both with the variant of the \line-command provided in your example and with the variant of the \line-command provided in the example above you get a lot of overfull \hbox-warnings because the dot-filled lines via \makebox get the with \linewidth while not taking the insertion of the horizontal \parindent-glue into account.
  • As your example works with \vskip you may wish to ensure vertical mode.
  • You also may wish to avoid the insertion of horizontal \parindent-glue and vertical \parskip-glue by not letting TeX enter unrestricted horizontal mode while typesetting the lines.
  • The vertical distance between lines will not be 5mm but will be 5mm+\baselineskip. This can be rectified by applying \offinterlineskip.
  • It is not clear to me what is intended by \setlength{\rightmargin}{5em}.

I suppose you wish a command where you can specify

  • the amount of lines—in the example below this is the mandatory argument of \lines,
  • the distance between lines—in the example below this is the Distance between lines=...-key of the optional argument of \lines (default is \dimexpr5mm+\baselineskip\relax),
  • a \vskip increasing the distance between the bottom of the box forming the previous line of text and the first dotted line—in the example below this is the Distance at top=...-key of the optional argument of \lines (default is 8mm) ,
  • a \vskip increasing the distance between the last dotted line and the top of the box forming the next line of text—in the example below this is the Distance at bottom=...-key of the optional argument of \lines (default is 8mm),
  • a \vskip increasing the the distance between the bottom of the box forming the previous line of text and the top of the box forming the next line of text in case no dotted line is typeset at all—in the example below this is the Gap if no dotted lines=...-key of the optional argument of \lines (default is 8mm),
  • indenting of the dotted lines from the right—in the example below this is the Indent from the right=...-key of the optional argument of \lines (default is 0em),
  • indenting of the dotted lines from the left—in the example below this is the Indent from the left=...-key of the optional argument of \lines (default is 0em).

 

\documentclass{article}
\usepackage{keyval}
\usepackage{multido}

\makeatletter
\newcommand\exchange[2]{#2#1}%
\@ifdefinable\lines@DistanceBetweenLines{\edef\lines@DistanceBetweenLines{\the\dimexpr5mm+\baselineskip\relax}}%
\define@key{dottedlines}{Distance between lines}{\edef\lines@DistanceBetweenLines{\the\dimexpr#1\relax}}%
\@ifdefinable\lines@DistanceAtTop{\edef\lines@DistanceAtTop{\the\dimexpr8mm\relax}}%
\define@key{dottedlines}{Distance at top}{\edef\lines@DistanceAtTop{\the\dimexpr#1\relax}}%
\@ifdefinable\lines@DistanceAtBottom{\edef\lines@DistanceAtBottom{\the\dimexpr8mm\relax}}%
\define@key{dottedlines}{Distance at bottom}{\edef\lines@DistanceAtBottom{\the\dimexpr#1\relax}}%
\@ifdefinable\lines@GapIfNoLines{\edef\lines@GapIfNoLines{\the\dimexpr8mm\relax}}%
\define@key{dottedlines}{Gap if no dotted lines}{\edef\lines@GapIfNoLines{\the\dimexpr#1\relax}}%
\@ifdefinable\lines@IndentFromTheRight{\edef\lines@IndentFromTheRight{\the\dimexpr0em\relax}}%
\define@key{dottedlines}{Indent from the right}{\edef\lines@IndentFromTheRight{\the\dimexpr#1\relax}}%
\@ifdefinable\lines@IndentFromTheLeft{\edef\lines@IndentFromTheLeft{\the\dimexpr0mm\relax}}%
\define@key{dottedlines}{Indent from the left}{\edef\lines@IndentFromTheLeft{\the\dimexpr#1\relax}}%
\newcommand{\lines}[2][]{%
  \begingroup
  \ifvmode\else\par\fi
  \setkeys{dottedlines}{#1}%
  \offinterlineskip
  \if Y\expandafter\exchange\expandafter{\romannumeral\number\number#2 0}%
       {\expandafter\@firstofone\expandafter{\romannumeral-\number\number#2 0}}Y%
    \expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
  {%
    \vskip\dimexpr\lines@DistanceAtTop-\lines@DistanceBetweenLines\relax
    \multido{}{#2}{%
      \vskip\lines@DistanceBetweenLines
      \hbox to\hsize{\kern\lines@IndentFromTheLeft\relax\dotfill\kern\lines@IndentFromTheRight\relax\null}%
    }%
    \vskip\lines@DistanceAtBottom\relax
  }%
  {\vskip\lines@GapIfNoLines\relax}%
  \par
  \endgroup
}%
\makeatother

\begin{document}

\hrule\kern\dp\strutbox

\verb*|\somecount=3|:

\verb*|\lines{\somecount}|:

\newcount\somecount
\somecount=3

\lines{\somecount}

\hrule\kern\dp\strutbox

\verb*|\lines{\the\somecount}|:

\lines{\the\somecount}

{
\hrule\kern\dp\strutbox

\verb*|\count90=3|

\verb*|\lines{\count90}|:

\count90=3 
\lines{\count90}

\hrule\kern\dp\strutbox
\kern-\topsep

\begin{verbatim*}
\lines[Distance between lines=5mm,
       Distance at top=1cm,
       Distance at bottom=1cm,
       Gap if no dotted lines=0cm,
       Indent from the left=2cm,
       Indent from the right=4cm]{\count90 }:
\end{verbatim*}
\kern-\topsep\kern-\partopsep
\lines[Distance between lines=5mm,
       Distance at top=1cm,
       Distance at bottom=1cm,
       Gap if no dotted lines=0cm,
       Indent from the left=2cm,
       Indent from the right=4cm]{\count90 }
\hrule\kern\dp\strutbox
}

\newpage

\hrule\kern\dp\strutbox

\verb*|\lines{3}|:

\lines{3}

\hrule\kern\dp\strutbox

\verb*|\lines{ 3 }|:

\lines{ 3 }

\hrule\kern\dp\strutbox

\verb*|\lines{-000003}|:

\lines{-000003}

\hrule\kern\dp\strutbox

\verb*|\lines{}|:

\lines{}

\hrule\kern\dp\strutbox

\verb*|\lines{ }|:

\lines{ }

\hrule\kern\dp\strutbox

\verb*|\lines{0}|:

\lines{0}

\hrule\kern\dp\strutbox

\end{document}

enter image description here

enter image description here

(Any sequence yielding \lines{⟨optional signs⟩} is treated as \lines{0}.)