[C++] Da $ a \(
Ciao. Volevo scrivere una funzione [inline]latex2e_fy[/inline] che data una stringa come
Non so bene come fare. Ho scritto un prototipo che produce
Inoltre conosco pochissimo il linguaggio e non ho idea se quello che ho scritto è buon codice (in particolare se avete voglia di spiegarmi come si potrebbe fare una cosa del genere in C++ moderno ciò è molto ben accetto).
Come bonus: come si potrebbe fare una cosa del genere in (buon) Python (3)?
Sia $ G $ un gruppo.ritornasse la stringa
Sia \( G \) un gruppo.
Non so bene come fare. Ho scritto un prototipo che produce
Sia ( G ) un gruppo.perché non so bene come trattare i due caratteri di [inline]\([/inline].
Inoltre conosco pochissimo il linguaggio e non ho idea se quello che ho scritto è buon codice (in particolare se avete voglia di spiegarmi come si potrebbe fare una cosa del genere in C++ moderno ciò è molto ben accetto).
#include <iostream> #include <string> std::string latex2e_fy(std::string s) { bool d = false; for(std::string::iterator c = s.begin(); c != s.end(); ++c) { if(*c == '$' && d == false) { d = true; *c = '('; } if(*c == '$' && d == true) { *c = ')'; d = false; } } return s; } int main() { std::cout << latex2e_fy("Sia $ G $ un gruppo."); return 0; }
Come bonus: come si potrebbe fare una cosa del genere in (buon) Python (3)?
Risposte
Anche tu lo odi quando devi scrivere qui, vero?
Se usi vim fai come me: installa Ultisnips https://github.com/SirVer/ultisnips e poi definisci uno snippet così:
PS: C++? A meno che tu non debba programmare un videogioco o un semaforo, eviterei.
Se usi vim fai come me: installa Ultisnips https://github.com/SirVer/ultisnips e poi definisci uno snippet così:
snippet '\$(.+?)\$' "mentify text" wrA \(`!p snip.rv = match.group(1)`\) endsnippet(questo è anche la risposta alla domanda bonus, perché stai usando una regex e del pyhton basilare per trasformare ogni gruppo [inline]$culo$[/inline] in [inline]\(culo\)[/inline] mentre scrivi).
PS: C++? A meno che tu non debba programmare un videogioco o un semaforo, eviterei.
Ciao.
- se ho ben capito vuoi che le sottostringhe
siano sostituite con
, giusto?
- il carattere $ può comparire nella stringa solo con l'accezione sopra evidenziata?
- la stringa in input può essere ritenuta sintatticamente corretta oppure andrebbe comunque controllata?
- se ho ben capito vuoi che le sottostringhe
$ "something" $
siano sostituite con
\( "something" \)
, giusto?
- il carattere $ può comparire nella stringa solo con l'accezione sopra evidenziata?
- la stringa in input può essere ritenuta sintatticamente corretta oppure andrebbe comunque controllata?
"megas_archon":Ahah sì, ma almeno i [inline]\([/inline], [inline]\)[/inline] sono LaTeX standard.
Anche tu lo odi quando devi scrivere qui, vero?
Comunque volevo solo vedere se mi ricordavo come si scrivevano due righe di codice. Credo che si potesse fare anche con una cosa come https://www.gnu.org/software/emacs/manu ... place.html, ma la voglia di imparare le regex oggi non c'è.
@utente_medio Rispondo "Sì" a tutte le tue domande, anche se ovviamente non ho un'idea ben precisa di come dovrebbe essere fatta una stringa ben formata.
"megas_archon":Oddio, assieme alla STL non è meno scriptabile di Python. Credo che in un linguaggio tipo Haskell invece la soluzione sia uno one-liner...
PS: C++? A meno che tu non debba programmare un videogioco o un semaforo, eviterei.
Ma anche in bash è uno oneliner, eh...
Adesso non ho tempo di scrivere tutto per bene, ma quello che vuoi è parsare il tuo file cercando cose del tipo [inline]\$[^\$\n]{1,}\n[/inline] e alzando un errore se ce ne sono: se ce ne sono significa che c'è una cosa del genere
e una regex "stupida" non può gestire queste situazioni.
Quindi, come si fa questo in python? Non lo so, ma non penso sia molto diverso da un try-catch con un messaggio di errore personalizzato...
Quindi è meglio vietarle all'origine e chiedere all'user (tu o altri) di presentare solo un file dove il dollaro "che apre" e quello "che chiude" sono sulla stessa riga.
Fatto questo, la regex è la stessa, cerchi [inline]\$(.+?)\$[/inline] e vuoi sostituirlo con [inline]\($1\)[/inline]. A seconda del linguaggio in cui scrivi cambiano le wildcards delle espressioni regolari e anche il modo specifico di implementarli (e.g. dove stanno di preciso i gruppi che la regex catcha) quindi non provo nemmeno a farlo oggi, tanto piu che sta saltando la luce ogni dieci minuti...
$matematica bellissima ma dollari che non matchano$
e una regex "stupida" non può gestire queste situazioni.
Quindi, come si fa questo in python? Non lo so, ma non penso sia molto diverso da un try-catch con un messaggio di errore personalizzato...
Quindi è meglio vietarle all'origine e chiedere all'user (tu o altri) di presentare solo un file dove il dollaro "che apre" e quello "che chiude" sono sulla stessa riga.
Fatto questo, la regex è la stessa, cerchi [inline]\$(.+?)\$[/inline] e vuoi sostituirlo con [inline]\($1\)[/inline]. A seconda del linguaggio in cui scrivi cambiano le wildcards delle espressioni regolari e anche il modo specifico di implementarli (e.g. dove stanno di preciso i gruppi che la regex catcha) quindi non provo nemmeno a farlo oggi, tanto piu che sta saltando la luce ogni dieci minuti...
"marco2132k":
@utente_medio Rispondo "Sì" a tutte le tue domande
In tal caso penso che qualcosa del genere potrebbe andare:
#include <iostream> #include <string> using namespace std; string fun(const string &s_in) { string s_out; for(unsigned int flag = false, i = 0; i < s_in.size(); ++i) { if(s_in[i] == '$') { s_out.push_back('\\'); s_out.push_back((flag = !flag) ? '(' : ')'); } else { s_out.push_back(s_in[i]); } } return s_out; } int main() { cout << fun("Sia $ G $ un gruppo.") << endl; }
import re pattern = re.compile("\$[^\$\n]{1,}\n") for i, line in enumerate(open('cose.txt')): for match in re.finditer(pattern, line): print( 'Unmatched dollar before newline on line %s: %s' % (i+1, match.group()))
Questo serve a trovare i dollari solitari. Ovviamente si può fare di meglio:
- dando contesto all'utente (printa la riga del match, la precedente, e la successiva)
- colorando il match di rosso
ma adesso ho voglia zero. Il resto è banale, e in effetti è uno script che già ho: adattalo da qui
#!/usr/bin/env python3 import re, os, sys good_delims = lambda x: re.sub(r"\$(.*?)\$", r"\\(\1\\)", x, flags = re.M) with open (sys.argv[1], 'r+' ) as f: content = f.read() content = good_delims(content) f.seek(0) f.write(content) f.truncate() os.system("latexindent -w " + sys.argv[1])
Fammi sapere se funonzia.
"megas_archon":In teoria se la stringa è "ben formata" (cioè è del LaTeX scritto nella vita reale) una cosa del genere può capitare solo se l'editor fa hard wrapping, no?
[È] meglio vietarle all'origine e chiedere all'user (tu o altri) di presentare solo un file dove il dollaro "che apre" e quello "che chiude" sono sulla stessa riga.
Poi provo se quello che hai scritto tu funzia ma faccio un po' fatica a capire cosa fa (perché non conosco bene né Python né appunto le regex).
@utente_medio Leggendo il tuo snippet mi è venuto in mente che la mia [inline]latex2e_fy[/inline] ha side effects. Ci riprovo.
#include <fstream> #include <iostream> #include <string> std::string latex2e_fy(const std::string& s_in) { bool d = false; std::string s_out; s_out.reserve(s_in.length()); for(auto c : s_in) { if(c == '$' && d == false) { s_out.append("\\("); d = !d; } else if (c == '$' && d == true) { s_out.append("\\)"); d = !d; } else { s_out.push_back(c); } } return s_out; } int main(int argc, char** argv) { if(argc < 3) { std::cout << "Utilizzo: latex2e_fy input file output file."; return 1; } std::ifstream f_in(argv[1]); std::ofstream f_out(argv[2]); if(f_in.is_open() && f_out.is_open()) { std::string current_line; while(std::getline(f_in,current_line)) { f_out << latex2e_fy(current_line) << std::endl; } } f_in.close(); f_out.close(); return 0; }
Questo apparentemente funzia.
Domanda: ho pensato di usare [inline]reserve[/inline] per migliorare le prestazioni. E invece le peggiora. Come mai?
[ot]Tipicamente, questa roba è da Perl. Ad esempio:
In Haskell comunque non è complicato: con [tt]interact[/tt] si può fare qualcosa di decente...[/ot]
#!/usr/bin/env perl -w # usage: $ ./this-script.pl < input.tex > output.tex my $delim = "\\("; my $line; while ($line = <STDIN>) { while ($line =~ /[^\\]\$/) { $line =~ s/([^\\])\$/$1$delim/; $delim = $delim eq "\\(" ? "\\)" : "\\("; } print $line; }
In Haskell comunque non è complicato: con [tt]interact[/tt] si può fare qualcosa di decente...[/ot]
Alcuni commenti:
Puoi scrivere anche meno codice di così (usando [tt]accumulate/reduce[/tt] e [tt]join[/tt] per esempio) ma ho cercato di renderlo leggibile. In C++ il codice sarebbe abbastanza simile.
[*:1jugthex] Non è possibile modificare una stringa con un'altra più lunga "in-place". È quindi sempre necessario avere un input e output separati in operazioni come queste.[/*:m:1jugthex]
[*:1jugthex] Le classi stringa sono raramente ottimizzate per operazioni come questa. In C++ sarebbe meglio fare uso di stringstream. In Python esistono classi simili come StringIO.[/*:m:1jugthex]
[*:1jugthex] Se si desidera che questo strumento funzioni correttamente è necessario considerare alcune situazioni particolari. Prima di tutto se il dollaro è preceduto da '\' allora va ignorato. TeX supporta poi anche '\$\$' per indicare una formula in una riga indipendente. Scopro ora che in realtà non è ufficialmente supportato in LaTeX ma ho l'impressione abbia sempre funzionato lo stesso. Almeno in teoria è poi necessario assicurarsi che il paragrafo non finisca all'interno dell'ambiente matematico perché indicherebbe un file LaTeX non corretto. Ovviamente non ha importanza se si tratta di un esercizio.[/*:m:1jugthex][/list:u:1jugthex]
In Python puoi scrivere qualcosa del genere (non l'ho testato):
from io import StringIO def latex2e_fy(s): output = StringIO() met_dollar = False # Iterate over the string an write the desired output to the StringIO object for c in s: if c != '$': output.write(c) else: output.write(r'\)' if met_dollar else r'\(') met_dollar = not met_dollar if met_dollar: # There is an odd number of dollars # You may want to handle an invalid input differently but I am lazy here return None result = output.getvalue() output.close() return result
Puoi scrivere anche meno codice di così (usando [tt]accumulate/reduce[/tt] e [tt]join[/tt] per esempio) ma ho cercato di renderlo leggibile. In C++ il codice sarebbe abbastanza simile.
[ot]Era stato menzionato Haskell. La cosa bella è che non hai bisogno di espressioni regolari o librerie assurde; ad esempio:
Probabilmente la cosa si può ottimizzare considerando [inline]ByteString[/inline]... ma già va bene.
Come lo script in Perl sopra, pensa a non trattare il dollaro escappato, cioè [inline]\$[/inline].[/ot]
-- L'uso è lo stesso dello script Perl sopra... main :: IO () main = interact change_delims change_delims :: String -> String change_delims = helper '(' where helper :: Char -> String -> String helper c ('\\':'$':str) = '\\':'$':helper c str helper c ('$':str) = (\d -> '\\':c:helper d str) (if c == '(' then ')' else '(') helper c (s:ss) = s:helper c ss helper _ "" = ""
Probabilmente la cosa si può ottimizzare considerando [inline]ByteString[/inline]... ma già va bene.
Come lo script in Perl sopra, pensa a non trattare il dollaro escappato, cioè [inline]\$[/inline].[/ot]
Non so se la cosa interessa ancora, o se tu usi *nix.
È una soluzione molto breve ma funzionante se non hai dollari pendenti sul tuo sorgente TeX né dollari escappati. (La soluzione in Perl che ho dato prima non si scrive in una riga, ma almeno si cura di questi due aspetti.) Per sicurezza viene creata una copia di backup con estensione ".bak"
Esempio:
Vedi questa documentazione di Perl.
$ perl -i.bak -lpe 's/\$([^\$]+)\$/\\($1\\)/g' hello.tex
È una soluzione molto breve ma funzionante se non hai dollari pendenti sul tuo sorgente TeX né dollari escappati. (La soluzione in Perl che ho dato prima non si scrive in una riga, ma almeno si cura di questi due aspetti.) Per sicurezza viene creata una copia di backup con estensione ".bak"
Esempio:
$ cat <<"EOF" > hello.tex A formula: $E = mc^2$. Some text. Another formula $a^2 + b^2 = c^2$. Yet another formula $\lambda x . x$. Blah blah blah... EOF $ perl -i.bak -lpe 's/\$([^\$]+)\$/\\($1\\)/g' hello.tex $ cat hello.tex A formula: \(E = mc^2\). Some text. Another formula \(a^2 + b^2 = c^2\). Yet another formula \(\lambda x . x\). Blah blah blah...
Vedi questa documentazione di Perl.