Skip to content

Come posso svelare la ricorsione apparente in una dichiarazione edef?

Mantieni la tua attenzione perché in questa divisione troverai la disposizione che stai cercando.

Soluzione:

Prima alcuni commenti generali sul vostro codice:

In LaTeX usa bfseries invece di bf e ttfamily al posto di tt. I comandi per il cambio di font a 2 lettere sono obsoleti poiché LaTeX2ε è stato rilasciato più di 20 anni fa.

globaledef può essere abbreviato in xdef. Tuttavia, una macro non crea un livello di gruppo, quindi, dato che si sta usando tutto questo senza alcun livello di raggruppamento, il comando global non sono necessari (a meno che, ovviamente, nel codice reale non siano necessari, allora ignorateli).

Un trucco per la stampa dei nomi dei comandi è che ciò che si ottiene con ttt command si può fare con ttfamilystringcommand o, se si definisce defttt{ttfamilystring}, allora tttcommand.

Una cosa molto importante è: non allocare un registro più di una volta. In questo modo si esaurisce il numero di registri disponibili. Allocate i registri una sola volta e usateli tutte le volte che ne avete bisogno. Ho spostato newcountchNum al di fuori della definizione della macro.

Nelle assegnazioni di numeri TeX capisce già chNum=`#1 per ottenere il valore numerico di una costante alfabetica; non è necessario (ma non fa nemmeno male) numexpr qui. Anche la coppia di parentesi in più intorno a `#1 nel campo numexpr non sono necessarie.

Passiamo ora a testAppendOne. Il codice

chNum=numexpr(`#1)%
chardefmyCh=chNum%
appendChar{charmyCh}%

aggiunge i due token charmyCh a resultStr. Tuttavia, poiché l'elemento char non è espandibile, il contenuto di resultStr sono charmyCh dopo la prima iterazione, charmyChcharmyCh dopo la seconda e così via. Quando si chiede a TeX di scrivere il contenuto di resultStr utilizzerà charmyChcharmyCh... e myCh avrà l'ultimo valore assegnato, che sarà l'ultima lettera elaborata, quindi il comportamento che si vede.

Quando si elabora amyCh è char"61, quindi charchar"61 sarà a. È abbastanza ovvio. Quando si elabora bmyCh è char"62 allora charchar"62charchar"62, che è bb e così via. Per risolvere questo problema è necessario dare appendChar qualcosa che non cambierà se il parametro myCh cambiano. Il codice

chNum=numexpr(`#1)%
chardefmyCh=chNum%
appendChar{charmyCh}%

è ridondante. Non è necessario chardef un carattere prima di passarlo a char. Come avete fatto voi stessi per il backslash con char92è possibile utilizzare il numero anche in questo caso:

chNum=`#1
appendChar{charchNum}%

o anche:

appendChar{char`#1}%

Questo passerà char` a appendChar e si otterrà ciò che si vuole.

Ma perché charthemyCh in testAppendTwo funziona? Perché la primitiva the accederà al valore in myCh. Supponiamo che myCh sia char"61 (a), allora themyCh sarà "61 (esadecimale) o 97. Quindi il codice

chNum=numexpr(`#1)%
chardefmyCh=chNum
showthemyCh
appendChar{charthemyCh}%

è ridondante anche perché themyCH accede al numero in myChche è chNum, quindi si può semplificare a:

chNum=numexpr(`#1)%
appendChar{charchNum}%

testAppendThree è lo stesso, tranne che per il fatto che thechNum otterrà il valore nel campo count invece del registro char. Ma sono uguali, quindi il risultato è lo stesso (ed è lo stesso della versione modificata di testAppendTwo :).


Passiamo ora alla seconda parte: come eseguire il debug del codice TeX... Beh... Non è il caso di entrare qui. Sarete più felici se non lo farete (sto scherzando... o forse sì?).

Il vostro codice è piuttosto semplice, quindi sono riuscito a eseguire il debug usando soltanto show e showthe. Entrambi sono usati come show e showthe.

show stampa il contenuto di una macro nella console. Per esempio, se modifico la macro appendChar in:

defappendChar#1{% Append argument character to string
  xdefresultStr{resultStr#1}%
  showresultStr
  par{ttfamily~RLp#1RRp~~~RLbresultStrRRb}par%
}

ed eseguo la macro testAppendOne TeX mostrerà questo nel terminale:

> resultStr=macro:
->char myCh .
appendChar #1->xdef resultStr {resultStr #1}show resultStr 
                                                                 par {ttfamily ~RLp #1RRp ~~~RLb resultStr RRb }par 
l.63 useMacro{testAppendOne}

? 

poi, se premo mostra:

> resultStr=macro:
->char myCh char myCh .
appendChar #1->xdef resultStr {resultStr #1}show resultStr 
                                                                 par {ttfamily ~RLp #1RRp ~~~RLb resultStr RRb }par 
l.63 useMacro{testAppendOne}

? 

(notare la seconda riga). Questo mi ha fatto capire perché il vostro testAppendOne stampava la stessa lettera più volte, come ho descritto prima.

showthe è simile, ma mostra il valore di un registro. Ad esempio, se si desidera visualizzare il valore del registro chNum si usa showthechNum.

Queste due macro hanno degli equivalenti che, invece di stampare nella console (e nel log), stampano nel PDF. meaning stampa la definizione del registro e the stamperà il suo valore.


Non c'è abbastanza debug?

È possibile utilizzare il comando tracingall che dice a TeX "dammi tutto ciò che hai da mostrare" (e poi tracingnone quando si vuole che si fermi), e la console sarà inondata di testo. Con un po' di pratica imparerete a leggerlo.

È anche possibile caricare il comando trace e usare traceon/traceoffche nasconde alcuni passaggi del codice che stampano testi di traccia piuttosto lunghi, come il sistema di selezione dei caratteri di LaTeX.


Non è ancora abbastanza?

Caricare usepackage{unravel} (dopo tutto avete chiesto di sbrogliare 🙂 e usare unravel{}e il pacchetto vi mostrerà passo dopo passo cosa sta facendo TeX. È piuttosto utile quando si è bloccati in qualche pezzo di codice difficile da gestire. Attenzione: questo passo-passo è veramente approfondito, quindi potrebbero essere necessari una manciata di passaggi per unravel{useMacro{appendChar}} 🙂

Si sta utilizzando

  chardefmyCh=chNum%
  appendChar{charmyCh}%

char né un chardef definito come myCh è espandibile, quindi il ripetuto edef produce semplicemente

charmyChcharmyChcharmyChcharmyCh

e quando finalmente si usa la macro si ottengono copie multiple dell'ultima macro definita. myCh

È possibile eseguire il debug aggiungendo qualcosa come

texttt{meaningresultStr}

all'output. Se lo si fa con il codice originale, si otterrà, per esempio, testAppendOne,

enter image description here

.testAppendOne,

enter image description here

Per testAppendTwo e testAppendThree si otterrebbe

enter image description here

che non è nemmeno quello che volete, vero?

Ecco una versione corretta di testAppendOne che aggiunge il carattere reale con un lowercase e cioè

deftestAppendOne#1{% Use a better approach; WORKS
  chNum=numexpr(`#1)relax
% Do arithmetic on the counter to implement the sorting rules
  begingrouplccode`!=chNumlowercase{endgroupappendChar{!}}%
}

Si notino altre correzioni: ttfamily è usato al posto di tt e allo stesso modo bfseries per bf; più importante, non si dovrebbe usare newcountchNum a ogni chiamata: si spreca un registro di conteggio a ogni chiamata di macro. Ho anche semplificato le macro iniziali.

Perché uso lccode`!=chNum? Il carattere ! ha il codice di categoria 12, quindi essenzialmente normalizziamo tutti i codici di categoria a 12, il che sembra appropriato quando si tratta di stringhe.

documentclass{article}

usepackage[svgnames]{xcolor}% to get named colors

newcommand{Rtt}[1]{textcolor{Red}{ttfamily#1}}
newcommandRLp{Rtt{(}}newcommandRRp{Rtt{)}} % input
newcommandRLb{Rtt{[}}newcommandRRb{Rtt{]}} % [output]

newcommandttt{{ttfamilychar92}} % A better tt textbackslash

newcountchNum % NOT inside macros

defappendChar#1{% Append argument character to string
  globaledefresultStr{resultStr#1}%
  par{ttfamily~RLp#1RRp~~~RLbresultStrRRb}par
}

deftestAppendOne#1{% Use a better approach; WORKS
  chNum=numexpr(`#1)relax
% Do arithmetic on the counter to implement the sorting rules
  begingrouplccode`!=chNumlowercase{endgroupappendChar{!}}%
}

deftestAppendTwo#1{% Use counter, chardef and the; DOESN'T REALLY WORK
  chNum=numexpr(`#1)relax
% Do arithmetic on the counter to implement the sorting rules
  chardefmyCh=chNum
  appendChar{charthemyCh}%
}

deftestAppendThree#1{% Bypass chardef, DOESN'T REALLY WORK
  chNum=numexpr(`#1)relax
% Do arithmetic on the counter to implement the sorting rules
  appendChar{charthechNum}%
}

defuseMacro#1{% Calls macro #1{}
  globaledefresultStr{}% (Re)initialize to empty string:
  medskip
  par{ttRLp chrRRp~~RLbttt resultStrRRb}par
  #1{a}% Simulate character data from string parsing macro
  #1{b}%
  #1{c}%
  #1{d}%
  #1{e}%
  medskip
  texttt{meaningresultStr}% for debugging
  medskip
}

begin{document} %               B E G I N   D O C U M E N T

% Proof of principle of using edef to rebuild a string
noindent{Largebfseriescolor{Blue}Using {ttt resultStrAppend}:}
useMacro{appendChar}

% First attempt fails with apparent recursion
bigskipnoindent{Largebfseriescolor{Blue}Using {ttt testAppendOne}:}
useMacro{testAppendOne}

% Second attempt: adding the command eliminates recursion
bigskipnoindent{Largebfseriescolor{Blue}Using {ttt testAppendTwo}:}
useMacro{testAppendTwo}

% Third (final) macro: don't need the chardef, have working macro
bigskipnoindent{Largebfseriescolor{Blue}Using {ttt testAppendThree}:}
useMacro{testAppendThree}

end{document}

enter image description here

Qui puoi vedere i commenti e le valutazioni dei lettori

Se capisci che il nostro post ti è stato utile, ti saremmo grati se lo condividessi con altri sviluppatori e ci aiutassi a diffondere i nostri contenuti.



Utilizzate il nostro motore di ricerca

Ricerca
Generic filters

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.