Символические функции: изменение каткодов и \tl_rescan:nn

Символические функции: изменение каткодов и \tl_rescan:nn

Цель состоит в том, чтобы пользователь ввел (для аргументации) линейную функцию от одной переменной, например, 2*x+3вместе с переменной (это не принципиально) и значением для переменной. Затем я хотел бы вычислить значение функции. Следующее работает, но теперь я думаю, что это работает скорее случайно, чем как-то еще.

  1. Моя первоначальная попытка \findvalueBне удалась и, похоже, мне не понравилось подчеркивание, поэтому я попробовал метод, \findvalueAкоторый сработал. Почему один из них работает, а другой нет?

  2. Принимая решение изменить имена переменных для тестирования, я столкнулся с очевидными проблемами (например, с использованием ybreaks ) \mytoks. Поэтому я попытался жестко закодировать имя переменной, и снова неудача. Основная идея, похоже, работает так, как показано внизу. Почему эта попытка не удалась?

Вероятно, есть общая тема в том, что я не понимаю, и это, скорее всего, фундаментальное непонимание чего-то, что я хотел бы исправить. Мне больше интересно объяснение, чем решение кода, хотя оба варианта были бы хороши.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\newtoks\mytoks

\NewDocumentCommand {\findvalueA} { m m m }
    {
        \group_begin:
        \char_set_catcode_active:N #1
        \tl_rescan:nn{}{\def#1{#2}}
        \tl_rescan:nn{}{\mytoks={#3}}
        \fp_set:Nn \l_tmpa_fp {\the\mytoks}
        \fp_use:N \l_tmpa_fp
        \group_end:
    }

\NewDocumentCommand {\findvalueB} { m m m }
    {
        \group_begin:
        \char_set_catcode_active:N #1
        \tl_rescan:nn{}{\def#1{#2}}
        % underscores=bad
        \tl_rescan:nn{}{\tl_set:Nn \l_tmpa_tl {#3}}
        % or
        % \tl_rescan:nn{}{\fp_eval:n {#3}}
        \fp_use:N \l_tmpa_fp
        \group_end:
    }

    \NewDocumentCommand {\findvalueC} { m m }
    {
        \group_begin:
        \char_set_catcode_active:N x
        \tl_rescan:nn {} {\defx{#1}}
        \tl_rescan:nn {} {\mytoks={#2}}
        \fp_set:Nn \l_tmpa_fp {\the\mytoks}
        \fp_use:N \l_tmpa_fp
        \group_end:
    }

\ExplSyntaxOff
\begin{document}

% works
\findvalueA{x}{5}{2*x+3}

% fails
%\findvalueB{x}{5}{2*x+3}

% fails
%\findvalueC{5}{x+2}

The method of findvalueC works down here but not above: if $x=5$, then $x+2=$
\ExplSyntaxOn
\group_begin:
\char_set_catcode_active:N x
\defx{5}
\fp_eval:n {x+2}
\group_end:
\ExplSyntaxOff

\end{document}

решение1

Неудача метода B довольно легко объяснима. Повторное сканирование материала считывает его с текущими применимыми кодами категорий. Когда выиспользовать \findvalueB, вы находитесь в документе, и поэтому _у него есть категория 'math subscript', а не 'letter' (вы также обнаружите, что это :неправильно, немного позже). Люди часто удивляются этому, поскольку они думают, что повторное сканирование будет использовать коды, установленные при определении команды. Это одна из вещей, которая делает использование повторного сканирования довольно сложным.

Неудачу метода C обнаружить сложнее. Здесь происходит то, что TeX «услужливо» вставляет пробел после \defxтого, как он выполняет повторное сканирование, так же, как он делает это после любой другой управляющей последовательности при изменении токенизации (например, в \writeили \detokenize). Это происходит «до» повторного сканирования, где \defxбудет один токен. Результатом является то, что в методе C xопределено, что за ним следует обязательный пробел: пробела нет, поэтому мы получаем ошибку. Этого не происходит в методе A, поскольку здесь пробел вставляется после \defи до #1(это отдельные токены до повторного сканирования). Таким образом, этот метод будет работать.

Обе эти проблемы показывают, почему повторное сканирование не приветствуется многими программистами TeX: я стараюсь избегать его. Мы предоставили разумные оболочки в LaTeX3, но ваши проблемы показывают, что все еще сложно сделать правильно. На самом деле вы хотите заменить все переменные буквы на значение. Поэтому я бы вообще не делал повторное сканирование, а использовал бы

\NewDocumentCommand { \findvalueD } { m m m }
  {
    \tl_set:Nn \l_tmpa_tl {#3}
    \tl_replace_all:Nnn \l_tmpa_tl {#1} {#2}
    \fp_eval:n { \l_tmpa_tl }
  }

который не имеет ни одной из этих проблем. (Я предполагаю, что у вас нет, например, sinв выражении и буквы nкак возможной переменной: такой случай можно было бы обработать, но это потребует дополнительных размышлений!)

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