Fibonacci macro
You should not allocate two new counters every time the macro is used (classic tex only has 256 counters, etex has more but still, ...)
If you do use counters you should allocate them just once, outside of the macro, and also whatever dubious example you may have seen elsewhere allocate your own counter do not re-use \@curtab
The question whether to use a loop or recursion doesn't really have an answer, as \loop
is simply a recursive macro, there is no looping primitive in TeX.
I'd probably do something like this (which would be expandable apart from the test for the optional argument)
\documentclass{article}
\makeatletter
\def\fib{\@ifnextchar[\fib@i{\fib@i[\relax]}}
\def\fib@i[#1]#2{%
\ifx\relax#1%
\fib@ii{#2}{#2}101%
\else
\fib@ii{#1}{#2}101%
\fi
}
% #1 hide if less than
% #2 target index
% #3 current index
% #4 #5 last two numbers
\def\fib@ii#1#2#3#4#5{%
\ifnum#1<\numexpr#3+1\relax
\the\numexpr#5\relax
\fi
\ifnum#3<#2\relax
\ifnum#1<\numexpr#3+1\relax, \fi
\fib@ii{#1}{#2}{\numexpr#3+1\relax}{#5}{\numexpr#4+#5\relax}%
\fi}
\makeatother
\begin{document}
%x\tracingall
\fib{20}
\fib[1]{10}
\end{document}
Since you're learning, I'll present a different (fully expandable) solution with expl3
:
\documentclass{article}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\fib}{O{#2}m}
{
\needle_fib:nn { #1 } { #2 }
}
\cs_new:Nn \needle_fib:nn
{% start the recursion from fib(1)=1, fib(2)=1
% #1 = current step in the recursion
% #2 = starting point
% #3 = end point
% #4 = fib(#1-2)
% #5 = fib(#1-1)
\needle_fib_print:nnnnn { 1 } { #1 } { #2 } { 1 } { 0 }
}
\cs_new:Nn \needle_fib_print:nnnnn
{
\int_compare:nT { #1 >= #2 }
{% the current step is at least the starting point, print the number
\int_eval:n { #4 + #5 }
% a comma-space if not at the last but one
\int_compare:nTF { #3 - #1 = 1 }
{% the first comparison is for the Oxford comma
\int_compare:nF { #3 - #2 = 1 } { , } ~and~ % before the last
}
{ \int_compare:nF { #3 = #1 } { ,~ } } % otherwise a comma-space
}
% recursion
\int_compare:nT { #1 < #3 }
{
\needle_fib_print:ennne
{ \int_eval:n { #1 + 1 } } % increase the step
{ #2 } % starting point
{ #3 } % end point
{ #5 } % previous fibonacci number
{ \int_eval:n { #4 + #5 } }
}
}
\cs_generate_variant:Nn \needle_fib_print:nnnnn { ennne }
\ExplSyntaxOff
\begin{document}
\fib{30}
\fib[19]{20}
\fib[1]{30}
\edef\test{\fib[1]{5}}
\texttt{\meaning\test}
\fib[1]{46}
\end{document}
The 47th Fibonacci number is beyond the arithmetic capabilities of TeX, unless you use bigintcalc
as shown in another answer of mine (which is a variant of this one).
\documentclass{article}
\usepackage[margin=1cm,a4paper]{geometry}
\usepackage{bigintcalc,siunitx}
\ExplSyntaxOn
\cs_set_eq:NN \needle_bigint_add:nn \bigintcalcAdd
\NewExpandableDocumentCommand{\fib}{O{#2}m}
{
\needle_fib:nn { #1 } { #2 }
}
\cs_new:Nn \needle_fib:nn
{% start the recursion from fib(1)=1, fib(2)=1
% #1 = current step in the recursion
% #2 = starting point
% #3 = end point
% #4 = fib(#1-2)
% #5 = fib(#1-1)
\needle_fib_print:nnnnn { 1 } { #1 } { #2 } { 1 } { 0 }
}
\cs_new:Nn \needle_fib_print:nnnnn
{
\int_compare:nT { #1 >= #2 }
{% the current step is at least the starting point, print the number
\needle_bigint_add:nn { #4} { #5 }
% a comma-space if not at the last but one
\int_compare:nTF { #3 - #1 = 1 }
{% the first comparison is for the Oxford comma
\int_compare:nF { #3 - #2 = 1 } { , } ~and~ % before the last
}
{ \int_compare:nF { #3 = #1 } { ,~ } } % otherwise a comma-space
}
% recursion
\int_compare:nT { #1 < #3 }
{
\needle_fib_print:ennne
{ \int_eval:n { #1 + 1 } } % increase the step
{ #2 } % starting point
{ #3 } % end point
{ #5 } % previous fibonacci number
{ \needle_bigint_add:nn { #4 } { #5 } }
}
}
\cs_generate_variant:Nn \needle_fib_print:nnnnn { ennne }
\ExplSyntaxOff
\begin{document}
The 200th Fibonacci number is \num{\fib{200}}
\fib{30}
\fib[19]{20}
\fib[1]{30}
\edef\test{\fib[1]{5}}
\texttt{\meaning\test}
\begin{flushleft}
\fib[1]{200}
\end{flushleft}
\end{document}
In the picture I only show the first part, the rest is the same as in the other picture. Using \num
shows by itself that the macro is fully expandable.
-My question is how to use
\def
and get an optional argument?
You can implement a loop based on \futurelet
for removing space tokens and looking at the next non-space-token for finding out if [
denoting an optional argument is present. This is what \@ifnextchar
of the LaTeX 2ε-kernel does.
\catcode`\@=11
\long\def\@firstofone#1{#1}%
\long\def\@ifnextchar#1#2#3{%
\let\reserved@d=#1%
\def\reserved@a{#2}%
\def\reserved@b{#3}%
\futurelet\@let@token\@ifnch
}%
\def\@ifnch{%
\ifx\@let@token\@sptoken
\let\reserved@c\@xifnch
\else
\ifx\@let@token\reserved@d
\let\reserved@c\reserved@a
\else
\let\reserved@c\reserved@b
\fi
\fi
\reserved@c
}%
\@firstofone{\def\@xifnch} {\futurelet\@let@token\@ifnch}%
\def\MacroWithOptArg{%
\@ifnextchar[{\InternalMacroWithOptArg}{\InternalMacroWithNoOptArg}%
}%
\long\def\InternalMacroWithNoOptArg#1{\InternalMacroWithOptArg[{#1}]{#1}}
\long\def\InternalMacroWithOptArg[#1]#2{%
This is the optional argument: #1.
This is the mandatory argument: #2.
}%
\MacroWithOptArg[A]{B}
\MacroWithOptArg{B}
\bye
\MacroWithOptArg
at some stage of expansion yields \@ifnextchar
which in turn yields a bunch of \def
- and \futurelet
- and \let
-assignments and therefore is not expandable. Therefore, like \@ifnextchar
, \MacroWithOptArg
is not expandable, too.
(A macro being "expandable" means that that all processing triggered by this macro is based on expansion only. In Knuth's analogy where TeX is an organism with eyes and a digestion-tract, all processing triggered by an expandable macro is done in the gullet, where expansion takes place. A macro being "not expandable" means that not just the gullet but also the stomach is involved in the processing. The stomach is the place where assignments are carried out etc.)
Unlike this example, the \newcommand
-macro of the LaTeX 2ε-kernel makes macros with optional arguments robust so that in so called "pure expansion contexts" they are not carried out as carrying them out in pure expansion contexts fails.
-Also, i was wondering if there was a better way that to check after each print
\ifnum>3
,\ifnum=3
and, so that i get the correct display (i.e. commas except for the last number 'and').
Off the cuff I don't see a better way with your code. But \fib@i
recursively calls itself, nested between \ifnum..\fi
. With each iteration another \fi
will be accumulated in the input-stack. If you do \expandafter\fib@i\fi
, the \fi
won't be accumulated as they get expanded (and hereby removed) before the next instance of \fib@i
is carried out. Besides this I modified your emptiness-test so that \expandafter
is not needed and it is not relied on \relax
not being redefined in terms of \outer
—which would be a very weird thing to do, but who knows...
\documentclass{article}
\makeatletter
\newcount\print@limit \newcount\@limit
\newcommand\fib[2][]{%
\ifcat$\detokenize{#1}$%no input#1
\print@limit=#2%
\else
\print@limit=#1%
\fi
\@tempcnta\@ne \@tempcntb\@ne \count@#2 \@limit\tw@
\ifnum\print@limit<\tw@ \number\@tempcntb, \fi
\fib@i}
\def\fib@i{%
\ifnum\count@>\@ne
\print@fib
\@curtab=\numexpr\@tempcnta+\@tempcntb\relax
\@tempcnta\@tempcntb \@tempcntb\@curtab
\advance\count@-\@ne \advance\@limit\@ne
\expandafter % <- !!!!!!!!!!
\fib@i
\fi}
\def\print@fib{%
\unless\ifnum\@limit<\print@limit%
\number\@tempcntb%
\ifnum\count@>\thr@@ , %
\else\ifnum\count@=\thr@@ ~and %
\fi\fi\fi}
\makeatother
\begin{document}
\fib{20}
\fib[1]{10}
\end{document}
However, i was wondering if it is better to use recursion or loop in this case?
\loop
is a recursive macro. ;-)
But it has some shortcomings:
- Its argument, which is delimited by
\repeat
, goes into a temporary macro. Therefore if\loop
is used to define macros that process arguments, hash-doubling is needed with these arguments. - You'd better not nest
\loop..\repeat
-constructs unless you know exactly what you do. The likelihood is high that the delimiter-matching with the argument-delimiter\repeat
won't work in the way expected by you. ;-)
As egreg proposed a full-expandable solution with expl3, I see the need of exhibiting shortcomings of expandable checking for the presence of optional arguments:
\documentclass{article}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\macro}{O{#2}m}{
\message{^^J
This~is~the~optional~argument:~[#1]^^J
This~is~the~non~optional~argument:~{#2}^^J^^J
}
}
\ExplSyntaxOff
\begin{document}
\macro[optional]{non-optional}
\macro{[}{optional}]{non-optional}
% The resulting messages should differ but don't.
\end{document}
I get the message
This is the optional argument: [optional]
This is the non optional argument: {non-optional}
twice.
I expected the second message to be
This is the optional argument: [[]
This is the non optional argument: {[}
and the sequence {optional}]{non-optional}
to yield the phrase optional]non-optional
within the resulting .pdf-file.
For the sake of having fun I implemented a variant where \fib
can also handle negative numbers and where internally an expandable tail-recursive macro \fibloop
is called.
ε-TeX's \numexpr
is used for doing the arithmetic and ε-TeX's \detokenize
is used for checking emptiness of an argument.
In case the upper bound is smaller than the lower bound, no numbers will be printed at all.
\documentclass{article}
\makeatletter
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\fib[2][]{%
\expandafter\UD@Exchange\expandafter{\expandafter{\expandafter>\number#2}}{%
\expandafter\UD@Exchange\expandafter{\expandafter{%
\expandafter<\number\ifcat$\detokenize{#1}$%
\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi{#2}{#1}%
}}{\fibloop{0}{0}{1}{}{}}%
}{+}%
}%
\newcommand\fibloop[8]{%
% #1 = n
% #2 = n-th Fibonacci-number
% #3 = (n+1)-th/(n-1)-th fibonacci number
% #4 = comma-space-separated list of Fibonacci-numbers found so far
% #5 = separator to prepend / append
% #6 = < lower bound of range / > upper bound of range = condition for appending Fibonacci-number to list of fibonacci-numbers found so far
% #7 = > upper bound of range / < lower bound of range = condition for ending the loop
% #8 = + / - = operator of arithmetic operations
\ifnum#1#7 \expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi{%
\ifx#8+\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
{\fibloop{-1}{1}{-1}{#4}{#5}{#7}{#6}{-}}{#4}%
}{%
\ifnum#1#6 \expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
{\UD@Exchange{{}{}}}{%
\ifx#8+\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
{\UD@Exchange{{#4#5#2}{, }}}{\UD@Exchange{{#2#5#4}{, }}}%
}{%
\expandafter\UD@Exchange\expandafter{\expandafter{\number\numexpr#2#8#3\relax}}{%
\expandafter\fibloop\expandafter{\number\numexpr#1#81\relax}{#3}%
}%
}{#6}{#7}{#8}%
}%
}%
\makeatother
\parindent=-1.5cm
\begin{document}
\begin{tabular}{ll}
\verb|\fib{20}|:&\fib{20}\\
\verb|\fib[]{20}|:&\fib[]{20}\\
\verb|\fib[20]{20}|:&\fib[20]{20}\\
\\
\verb|\fib[1]{10}|:&\fib[1]{10}\\
\\
\verb|\fib[-10]{10}|:&\fib[-10]{10}\\
\verb|\fib[0]{10}|:&\fib[0]{10}\\
\verb|\fib[10]{20}|:&\fib[10]{20}\\
\verb|\fib[5]{10}|:&\fib[5]{10}\\
\verb|\fib[-8]{-8}|:&\fib[-8]{-8}\\
\verb|\fib[-8]{-4}|:&\fib[-8]{-4}\\
\verb|\fib[-1]{0}|:&\fib[-1]{0}\\
\verb|\fib{-1}|:&\fib{-1}\\
\verb|\fib[-1]{-1}|:&\fib[-1]{-1}\\
\verb|\fib[]{-4}|:&\fib[]{-4}\\
\verb|\fib[0]{0}|:&\fib[0]{0}\\
\verb|\fib{0}|:&\fib{0}\\
\verb|\fib{1}|:&\fib{1}\\
\verb|\fib[0]{1}|:&\fib[0]{1}\\
\verb|\fib[1]{1}|:&\fib[1]{1}\\
\\
\verb|\fib[-4]{-8}|:&\fib[-4]{-8}\\
\verb|\fib[3]{0}|:&\fib[3]{0}\\
\verb|\fib[8]{3}|:&\fib[8]{3}\\
\verb|\fib[3]{-8}|:&\fib[3]{-8}\\
\end{tabular}
\end{document}