斐波那契宏

斐波那契宏

我正在尝试制作斐波那契宏,其工作原理如下。

在此处输入图片描述

我可以这样做,并且我尝试使用将所有内容设为私有@(我知道这里没有必要,但我正在尝试练习……)。我知道这\@curtab应该用于标签,但是因为我看到@David Carlisle 将它用于其他用途,所以我尝试做同样的事情。

这是我写的代码

\begin{document}

\makeatletter

\renewcommand\fib[2][]{\newcount\print@limit \newcount\@limit
    \expandafter\ifx\expandafter\relax\detokenize{#1}\relax %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
        \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

\end{document}

m AND n它工作得很好,我甚至通过将最后的打印改为而不是使它变得更好m, n。我的问题是我无法定义\fib使用\def,并且不得不求助于使用,\newcommand因为我无法弄清楚如何使用 获取可选参数\def

三个问题

  1. 我的问题是如何使用\def和获取可选参数?
  2. 另外,我想知道是否有更好的方法在每次打印后进行检查\ifnum>3 , \ifnum=3 and,以便我得到正确的显示(即除了最后一个数字“and”之外的逗号)。
  3. 最后,\loop ... \repeat我没有使用 ,而是定义了一个\fib@i,它的作用类似于\loop。但是,我想知道在这种情况下,使用递归还是循环更好?在另一种代码语言中,我会避​​免使用递归,因为它只能支持这么多的调用,但在这里,在发生这种情况之前就已经达到了算术溢出,所以我想知道一种方法是否比另一种更好。

答案1

您不应该在每次使用宏时分配两个新计数器(经典 tex 只有 256 个计数器,etex 有更多但仍然......)如果您确实使用计数器,您应该只分配一次,在宏之外,以及您可能在其他地方看到的任何可疑示例,分配您自己的计数器不要重复使用\@curtab

是否使用循环或递归的问题实际上没有答案,因为它\loop只是一个递归宏,TeX 中没有循环原语。

我可能会做这样的事情(除了可选参数的测试之外还可以扩展)

在此处输入图片描述

\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}

答案2

因为您正在学习,所以我将提出一个不同的(完全可扩展的)解决方案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}

在此处输入图片描述

第 47 个斐波那契数超出了 TeX 的算术能力,除非你bigintcalc使用我的另一个答案(这是这个的变体)。

\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}

在图片中我只显示了第一部分,其余部分与另一张图相同。使用\num本身就表明宏是完全可扩展的。

在此处输入图片描述

答案3

-我的问题是如何使用\def和获取可选参数?

您可以实现一个基于的循环,用于\futurelet删除空格标记并查看下一个非空格标记,以确定是否[存在表示可选参数的标记。这就是\@ifnextcharLaTeX 2ε 内核所做的。

\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在扩展的某个阶段会产生\@ifnextchar,这又会产生一堆\def- 和\futurelet- 和 -\let赋值,因此不可扩展。因此,像 一样\@ifnextchar\MacroWithOptArg也不可扩展。

(宏“可扩展”意味着该宏触发的所有处理都仅基于扩展。在 Knuth 的类比中,TeX 是一个有眼睛和消化道的有机体,可扩展宏触发的所有处理都在食道中完成,扩展就发生在食道中。宏“不可扩展”意味着不仅食道而且胃也参与处理。胃是执行作业的地方等。)

与此示例不同,\newcommandLaTeX 2ε-内核的 -macro 使具有可选参数的宏变得健壮,因此在所谓的“纯扩展上下文”中它们不会执行,因为在纯扩展上下文中执行它们会失败。

-另外,我想知道是否有更好的方法在每次打印后检查\ifnum>3\ifnum=3并且,以便我得到正确的显示(即除了最后一个数字“和”之外的逗号)。

即兴地,我看不出有更好的方法处理您的代码。但\fib@i递归调用自身,嵌套在之间\ifnum..\fi。每次迭代时,另一个\fi将在输入堆栈中累积。如果您这样做\expandafter\fib@i\fi,则在执行下\fi一个实例之前,它们会扩展(并因此被删除),因此不会累积。除此之外,我修改了您的空虚测试,因此不需要它,并且它不依赖于不重新定义——这将是一件非常奇怪的事情,但谁知道呢……\fib@i\expandafter\relax\outer

\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}

但是,我想知道在这种情况下使用递归还是循环更好?

\loop是一个递归宏。;-)

但它也有一些缺点:

  • 它的参数以 分隔\repeat,并进入临时宏。因此,如果\loop使用 来定义处理参数的宏,则需要对这些参数进行哈希加倍。
  • 除非您确切知道自己在做什么,否则最好不要嵌套\loop..\repeat构造。分隔符与参数分隔符的匹配很可能\repeat不会按照您期望的方式进行。;-)


由于 egreg 提出了一个带有 expl3 的完全可扩展解决方案,我认为需要展示可扩展检查是否存在可选参数的缺点:

\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}

我明白了

This is the optional argument: [optional]
This is the non optional argument: {non-optional}

两次。

我预计第二条消息是

This is the optional argument: [[]
This is the non optional argument: {[}

以及在结果 .pdf 文件中{optional}]{non-optional}产生短语的序列。optional]non-optional



为了好玩,我实现了一个变体,它\fib也可以处理负数,并且在内部\fibloop调用一个可扩展的尾递归宏。ε-TeX\numexpr用于进行算术运算,ε-TeX\detokenize用于检查参数是否为空。
如果上界小于下界,则根本不会打印任何数字。

\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}

在此处输入图片描述

相关内容