Come sostituire ogni partita con il contatore incrementale?


14

Voglio cercare e sostituire ogni occorrenza di un certo modello con un numero decimale che inizia 1e si incrementa di uno per ogni corrispondenza.

Riesco a trovare domande formulate in modo simile che risultano non riguardare l'incremento di un contatore ma la modifica di ogni partita di un importo fisso. Altre domande simili riguardano l'inserimento di numeri di riga anziché un contatore incrementale.

Esempio prima:

#1
#1.25
#1.5
#2

Dopo:

#1
#2
#3
#4

I miei dati reali hanno molto più testo in tutto ciò che voglio ri-numerare.


2
se hai perldo, puoi usare:%perldo s/#\K\d+(\.\d+)?/++$i/ge
Sundeep il

@Sundeep: avrei dovuto menzionare che sono su Windows 10 alla vaniglia, scusa!
hippietrail,

Risposte:


15

È necessaria la sostituzione con uno stato. Ricordo di aver fornito una (/ più?) Soluzione completa per questo tipo di problemi su SO.

Ecco un altro modo di procedere (1). Ora procederò in 2 passaggi:

  • una variabile lista fittizia di cui ho bisogno per il trucco sporco e contorto che userò
  • una sostituzione in cui inserisco la len di questa matrice fittizia che sto riempiendo per ogni occorrenza corrispondente.

Che dà:

:let t=[]
:%s/#\zs\d\+\(\.\d\+\)\=\ze/\=len(add(t,1))/g

Se non sei abituato a vim regex, io uso :h /\zse \zeper specificare quale sottotitolo sto abbinando, quindi abbino una serie di cifre eventualmente seguite da un punto e altre cifre. Questo non è perfetto per qualsiasi numero in virgola mobile, ma qui è abbastanza.

Nota: dovrai racchiuderlo in una coppia funzione + comando per una semplice interfaccia. Ancora una volta, ci sono esempi su SO / vim ( qui , qui , qui ) Oggi ne so abbastanza di vim da non preoccuparmi di avvolgere questo trucco in un comando. In effetti potrò scrivere questo comando al primo tentativo, mentre mi prenderò alcuni minuti per ricordare il nome del comando.


(1) L'obiettivo è di essere in grado di mantenere uno stato tra le sostituzioni e di sostituire l'occorrenza attuale con qualcosa che dipende dallo stato corrente.

Grazie a :s\=noi siamo in grado di inserire qualcosa risultante da un calcolo.

Resta il problema dello stato. O definiamo una funzione che gestisce uno stato esterno o aggiorniamo uno stato esterno. In C (e linguaggi correlati), avremmo potuto usare qualcosa di simile length++o length+=1. Sfortunatamente, negli script vim, +=non può essere utilizzato immediatamente. Deve essere usato con :seto con :let. Ciò significa che :let length+=1aumenta un numero, ma non restituisce nulla. Non possiamo scrivere :s/pattern/\=(length+=1). Abbiamo bisogno di qualcos'altro.

Abbiamo bisogno di funzioni mutanti. cioè funzioni che mutano i loro input. Noi abbiamo setreg(), map(), add()e probabilmente più. Cominciamo con loro.

  • setreg()muta un registro. Perfetto. Possiamo scrivere setreg('a',@a+1)come nella soluzione di @Doktor OSwaldo. Eppure, questo non è abbastanza. setreg()è più una procedura che una funzione (per quelli tra noi che conoscono Pascal, Ada ...). Ciò significa che non restituisce nulla. In realtà, restituisce qualcosa. L'uscita nominale (ovvero le uscite non eccezionali ) restituisce sempre qualcosa. Per impostazione predefinita, quando ci siamo dimenticati di restituire qualcosa, viene restituito 0 - vale anche per le funzioni integrate. Ecco perché nella sua soluzione l'espressione sostitutiva è in realtà \=@a+setreg(...). Ingannevole, vero?

  • map()potrebbe anche essere usato. Se partiamo da un elenco con un singolo 0 ( :let single_length=[0]), potremmo incrementarlo grazie a map(single_length, 'v:val + 1'). Quindi dobbiamo restituire la nuova lunghezza. Diversamente setreg(), map()restituisce il suo input mutato. È perfetto, la lunghezza è memorizzata nella prima (e unica, e quindi anche ultima) posizione dell'elenco. L'espressione sostitutiva potrebbe essere \=map(...)[0].

  • add()è quello che uso spesso per abitudine (ci ho pensato map()davvero, e non ho ancora messo in panchina le loro rispettive esibizioni). L'idea add()è quella di utilizzare un elenco come lo stato corrente e aggiungere qualcosa alla fine prima di ogni sostituzione. Memorizzo spesso il nuovo valore alla fine dell'elenco e lo uso per la sostituzione. Come add()riporta anche la sua lista di input mutato, possiamo usare: \=add(state, Func(state[-1], submatch(0)))[-1]. Nel caso di OP, dobbiamo solo ricordare quante partite sono state rilevate finora. Restituire la lunghezza di questo elenco di stati è sufficiente. Da qui il mio \=len(add(state, whatever)).


Penso di aver capito questo, ma perché il trucco con l'array e la sua lunghezza rispetto all'aggiunta di uno a una variabile?
hippietrail,

1
Questo perché si \=aspetta un'espressione e perché, diversamente da C, i+=1non è qualcosa che incrementa e restituisce un'espressione. Ciò significa che dietro \=ho bisogno di qualcosa che possa modificare un contatore e che restituisca un'espressione (uguale a quel contatore). Finora, le uniche cose che ho trovato sono le funzioni di manipolazione dell'elenco (e del dizionario). @Doktor OSwaldo ha utilizzato un'altra funzione di mutazione ( setreg()). la differenza è che setreg()non restituisce mai nulla, il che significa che restituisce sempre il numero 0.
Luc Hermitte,

Wow, interessante! Sia il tuo trucco che il suo sono così magici che penso che le tue risposte trarrebbero beneficio dallo spiegarle nelle tue risposte. Suppongo che solo i più bravi vignaioli conoscano tali idiomi poco intuitivi.
hippietrail,

2
@hippietrail. Spiegazioni aggiunte. Fammi sapere se hai bisogno di precisazioni più specifiche.
Luc Hermitte,

13
 :let @a=1 | %s/search/\='replace'.(@a+setreg('a',@a+1))/g

Ma attenzione, sovrascriverà il tuo registro a. Penso che sia un po 'più semplice della risposta di Luc, ma forse la sua è più veloce. Se questa soluzione è in qualche modo peggiore della sua, mi piacerebbe ricevere qualsiasi feedback sul perché la sua risposta sia migliore. Qualsiasi feedback per migliorare la risposta sarà molto apprezzato!

(Si basa anche su una mia risposta SO /programming/43539251/how-to-replace-finding-words-with-the-different-in-each-occurrence-in-vi-vim -edi / 43539546 # 43539546 )


Non vedo quanto @a+setreg('a',@a+1)sia più corto di len(add(t,1)). Altrimenti, questo è un altro bel trucco sporco :). Non ci ho pensato. Per quanto riguarda l'uso di una funzione di mutazione del dizionario nel testo sostitutivo, di :se substitute(), ho notato che questo è molto più veloce di cicli espliciti, quindi l' implementazione delle mie funzioni di elenco in lh-vim-lib . Immagino che la tua soluzione sarà alla pari con la mia, potrebbe essere un po 'più veloce, non lo so.
Luc Hermitte,

2
Per quanto riguarda le preferenze, preferisco la mia soluzione per un solo motivo: non viene @amodificata. Negli script, questo è importante IMO. In modalità interattiva, come utente finale, saprò quale registro posso usare. La confusione con un registro è meno importante. Nella mia soluzione, in modalità interattiva, viene incasinata una variabile globale; in uno script sarebbe una variabile locale.
Luc Hermitte,

@LucHermitte Siamo spiacenti, la mia soluzione non è in realtà più corta della tua, dovrei leggerla meglio prima di scrivere una simile dichiarazione. Ho rimosso la suddetta dichiarazione dalla mia risposta e vorrei scusarmi! Grazie per il tuo feedback interessante, lo apprezzo.
Doktor OSwaldo,

Non ti preoccupare. A causa della regex, è facile pensare che ci sia molto da scrivere. Inoltre, ammetto volontariamente che la mia soluzione è contorta. Sei il benvenuto per il feedback. :)
Luc Hermitte,

1
Davvero sei giusto. Il più delle volte estraggo un'altra informazione che immagazzino nell'ultima posizione dell'array, che è ciò che (l'ultimo elemento) inserisco alla fine. Ad esempio, per a +3, potrei scrivere qualcosa del genere \=add(thelist, 3 + get(thelist, -1, 0))[-1].
Luc Hermitte,

5

Ho trovato una domanda simile ma diversa che ho posto un paio di anni fa e sono riuscito a cambiare una delle sue risposte senza comprendere appieno quello che stavo facendo e ha funzionato benissimo:

:let i = 1 | g/#\d\+\(\.\d\+\)\=/s//\=printf("#%d", i)/ | let i = i+1

In particolare non capisco perché il mio non usi %o perché uso semplicemente una semplice variabile che le altre risposte evitano per qualche motivo.


1
anche questa è una possibilità. Penso che lo svantaggio principale qui sia l'uso di un comando sostitutivo per partita. Quindi è probabilmente più lento. Il motivo per cui non utilizziamo una variabile semplice è che non verrà aggiornato in una normale s//gistruzione. Comunque è una soluzione interessante. Forse @LucHermitte può dirti di più su pro e contro, dal momento che la mia conoscenza di vimscript è piuttosto limitata rispetto alla sua.
Doktor OSwaldo,

1
@DoktorOSwaldo. Immagino che questa soluzione abbia funzionato per un tempo più lungo - senza printf()pensarci - dato che gli Elenchi sono stati introdotti in Vim 7. Ma devo ammettere che non mi sarei aspettato (/ non ricordi?) Di <bar>appartenere allo scopo di :global- IOW, lo scenario che mi sarei aspettato era di applicare il :subsulle righe corrispondenti, quindi aumentare iuna volta alla fine. Mi aspetto che questa soluzione sia leggermente più lenta. Ma importa davvero? L'importante è quanto facilmente siamo in grado di trovare una soluzione funzionante da memoria + tentativi ed errori. Ad esempio, Vimgolfers preferisce le macro.
Luc Hermitte,

1
@LucHermitte sì, ho ecpexted lo stesso, e no la velocità non ha importanza. Penso che sia una buona risposta e di nuovo ho imparato qualcosa da esso. Forse il g/s//comportamento dell'ambito consente altri trucchi sporchi. Quindi grazie ad entrambi per le interessanti risposte e discussioni, spesso non imparo molto da dare una risposta =).
Doktor OSwaldo

4

Ci sono già tre grandi risposte in questa pagina, ma, come suggerito da Luc Hermitte in un commento , se stai facendo questo a sorpresa , la cosa importante è quanto velocemente e facilmente puoi trovare una soluzione funzionante.

In quanto tale, questo è un problema che non avrei usato :substituteper niente: è uno che può essere facilmente risolto usando i normali comandi in modalità normale e una macro ricorsiva:

  1. (Se necessario) Innanzitutto, disattivare 'wrapscan'. L'espressione regolare che useremo corrisponderà al testo del risultato desiderato e al testo iniziale, quindi con 'wrapscan'ON la macro continuerebbe a essere riprodotta per sempre. (O fino a quando non ti rendi conto di cosa sta succedendo e premi <C-C>.):

    :set nowrapscan
    
  2. Imposta il termine di ricerca (utilizzando la stessa espressione regolare di base già menzionata nelle risposte esistenti):

    /#\d\+\(\.\d\+\)\?<CR>
    
  3. (Se necessario) Torna alla prima partita premendo Ntutte le volte necessarie,

  4. (Se necessario) Cambia la prima corrispondenza nel testo desiderato:

    cE#1<Esc> 
    
  5. Cancella il "qregistro e inizia a registrare una macro:

    qqqqq
    
  6. Yank il contatore corrente:

    yiW
    
  7. Vai alla prossima partita:

    n
    
  8. Sostituisci il contatore corrente con quello appena tirato:

    vEp
    
  9. Incrementa il contatore:

    <C-A>
    
  10. Riproduci macro q. Il registro "qè ancora vuoto perché l'abbiamo eliminato nel passaggio 5, quindi a questo punto non succede nulla:

    @q
    
  11. Interrompere la registrazione della macro:

    q
    
  12. Riproduci la nuova macro e guarda!

    @q
    

Come per tutte le macro, questo appare come un sacco di passaggi quando spiegato come ho fatto sopra, ma nota che in realtà digitarli è molto veloce per me: a parte il ricorsivo-macro-registrazione-boilerplate sono tutti i normali comandi di modifica Sto eseguendo continuamente durante la modifica. L'unico passo in cui dovevo fare qualcosa anche avvicinandomi al pensiero era il passaggio 2, in cui ho scritto l'espressione regolare per eseguire la ricerca.

Formattato come due comandi in modalità riga di comando e una serie di sequenze di tasti, la velocità di questo tipo di soluzione diventa più chiara: posso evocare quanto segue abbastanza velocemente quanto posso scriverlo 1 :

:set nowrapscan
/#\d\+\(\.\d\+\)\?
cE#1<Esc>qqqqqyiWnvEp<C-A>@qq@q

Probabilmente avrei potuto escogitare altre soluzioni in questa pagina con un po 'di pensiero e qualche riferimento alla documentazione 2 , ma, una volta capito come funzionano le macro, sono davvero facili da sfornare a qualsiasi velocità tu modifichi di solito.

1: Ci sono situazioni in cui le macro richiedono più riflessioni, ma trovo che in pratica non escano molto. E generalmente le situazioni in cui si verificano sono quelle in cui una macro è l' unica soluzione pratica.

2: Non implicare che gli altri risponditori non avrebbero potuto trovare le loro soluzioni in modo simile facilmente: hanno solo bisogno di abilità / conoscenze che personalmente non ho così facilmente a portata di mano. Ma tutti gli utenti di Vim sanno come usare i normali comandi di modifica!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.