Вложенные \newcommand и \renewcommand

Вложенные \newcommand и \renewcommand

Читая класс, twentysecondcv.clsя заметил, что есть команда, определенная следующим образом:

\newcommand\skills[1]{
    \renewcommand{\skills}{
        \begin{tikzpicture}
            \foreach [count=\i] \x/\y in {#1}{
                \draw[fill=maingray,maingray] (0,\i) rectangle (6,\i+0.4);
                \draw[fill=white,mainblue](0,\i) rectangle (\y,\i+0.4);
                \node [above right] at (0,\i+0.4) {\x};
            }
        \end{tikzpicture}
    }
}

для чего используется вложенная \renewcommandвнутренняя часть \newcommand?

Полный классна GitHub.

решение1

Определение \skillsтаким образом приводит к тому, что при первом использовании ему требуется аргумент. Затем skillsон переопределяется для сохранения этого значения (плюс некоторая обработка), так что с этого момента команда (без аргумента) воспроизводит то, для чего она была настроена при первом вызове.

В качестве примера возьмем определение

\newcommand\myname[1]{\renewcommand\myname{#1}}

Когда вы выполняете

\myname{Stefano} % corresponds to \renewcommand\myname{Stefano}

то \mynameбудет означать Stefanoс этого момента и далее, т.е. каждое использование \mynameбудет набрано Stefano.

решение2

TeX не имеет переменныхкак таковойкак и другие языки программирования. Существуют специальные регистры для хранения таких вещей, как счетчики (целые числа), измерения, потоки ввода и вывода, списки токенов и т. д., но по большей части все определяется в терминах макросов. Это означает, что для сохранения информации в переменной вместо этого определяется макрос.

То, что было сделано здесь, неидиоматично и ограничено тем, что теперь команда \skillsизменила свое значение безвозвратно, и наивный пользователь класса обнаружит неожиданные результаты, если использует ее \skillsдважды. Обычно, вместо того, чтобы переопределять команду, можно было бы сделать что-то вроде этого:

\newcommand\skills[1]{
    \def\@skills{
        \begin{tikzpicture}
            \foreach [count=\i] \x/\y in {#1}{
                \draw[fill=maingray,maingray] (0,\i) rectangle (6,\i+0.4);
                \draw[fill=white,mainblue](0,\i) rectangle (\y,\i+0.4);
                \node [above right] at (0,\i+0.4) {\x};
            }
        \end{tikzpicture}
    }
}

который сохранит значение \@skillsдля последующего использования (в этом классе документа последующее использование происходит в \makeprofileопределении команды).

Более идиоматичным было бы, на самом деле, сделать

\newcommand\skills[1]{\def\@skills{#1}}

а затем перенести всю tikzpictureокружающую среду в \makeprofileопределение.

решение3

Команда \makeprofileопределяется как

\newcommand{\makeprofile}{
    \begin{tikzpicture}[remember picture,overlay]
        \node [rectangle, fill=sidecolor, anchor=north, minimum width=9cm, minimum height=\paperheight+1cm] (box) at (-5cm,0.5cm){};
    \end{tikzpicture}

    %------------------------------------------------

    \begin{textblock}{6}(0.5, 0.2)
[...irrelevant code...]
        \profilesection{Skills}

        \skills
        \skillstext
        \scriptsize
        %(*)[The skill scale is from 0 (Fundamental Awareness) to 6 (Expert).]
            
        %------------------------------------------------
            
    \end{textblock}
}

и вы должны сказать, \skills{x,y,z}прежде чем сделать \makeprofile.

Я не считаю это хорошим программированием, поскольку оно не позволяет проверять наличие ошибок.

Было бы лучше сделать

\newcommand{\skills}[1]{%
  \def\twentysecondscv@skills{...#1...}%
}

и в \makeprofile, вместо вызова\skills , делайте

\ifdefined\twentysecondscv@skills
  \twentysecondscv@skills
\else
  \ClassError{twentysecondscv}
    {No \protect\skills found}
    {You need to define \protect\skills before doing \protect\makeprofile}%
\fi

При использовании этого кода сообщение об ошибке будет содержать точную информацию о том, что именно пошло не так.

Также, \skillstextкажется, нигде не используется в предоставленных шаблонах. Если кто-то забудет \skills, то вызов \makeprofileпросто проглотит \skillstext. Если нет\skilltext команда не появляется, он сожрёт \scriptsize, который, кажется, существует только для того, чтобы быть проглоченным или полностью проигнорированным.

Даже лучше

\newcommand{\skills}[1]{%
  \def\twentysecondscv@skills{...#1...}%
  \renewcommand{\skills}[1]{\twentysecondscv@repeat{\skills}}%
}
\newcommand{\twentysecondscv@repeat}[1]{%
  \ClassError{twentysecondscv}
    {Multiple \protect#1 ignored}
    {You should have just one \protect#1 command}%
}

где\twentisecondscv@repeat может использоваться также для других команд, которые должны быть выполнены только один раз.

Связанный контент