Como substituir cada partida pelo contador de incremento?

14

Quero pesquisar e substituir cada ocorrência de um determinado padrão por um número decimal que comece em 1e incrementa em um para cada correspondência.

Posso encontrar perguntas com palavras semelhantes que não se destinam a incrementar um contador, mas a modificar cada partida por um valor fixo. Outras perguntas semelhantes são sobre a inserção de números de linha, em vez de um contador de incremento.

Exemplo, antes:

#1
#1.25
#1.5
#2

Depois de:

#1
#2
#3
#4

Meus dados reais têm muito mais texto em torno das coisas que quero renumerar.

hippietrail
fonte
2
Se você tem perldo, você pode usar:%perldo s/#\K\d+(\.\d+)?/++$i/ge
Sundeep
@ Sundeep: eu deveria ter mencionado que estou no Windows 10 com baunilha, desculpe!
Hippietrail

Respostas:

15

Você precisa de substituição por um estado. Lembro-me de ter fornecido uma (/ várias?) Solução completa para esse tipo de problemas no SO.

Aqui está outra maneira de proceder (1). Agora, vou prosseguir em 2 etapas:

  • uma variável de lista fictícia que preciso para o truque sujo e complicado que vou empregar
  • uma substituição na qual insiro o len desse array fictício que estou preenchendo em cada ocorrência correspondente.

Que dá:

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

Se você não está acostumado a vim regexes, eu uso :h /\zse \zepara especificar qual sub-padrão eu estou correspondendo, então eu correspondo a uma série de dígitos possivelmente seguidos por um ponto e outros dígitos. Isso não é perfeito para nenhum número de ponto flutuante, mas é o suficiente aqui.

Nota: Você precisará agrupá-lo em uma função dupla + comando para uma interface simples. Novamente, existem exemplos no SO / vim ( aqui , aqui , aqui ) Atualmente, conheço o suficiente do vim para não me importar em envolver esse truque em um comando. Na verdade, poderei escrever este comando na primeira tentativa, enquanto levarei alguns minutos para lembrar o nome do comando.


(1) O objetivo é poder manter um estado entre substituições e substituir a ocorrência atual por algo que dependa do estado atual.

Graças a :s\=nós somos capazes de inserir algo resultante de uma computação.

Continua sendo o problema do estado. Ou definimos uma função que gerencia um estado externo ou nos atualizamos como um estado externo. Em C (e idiomas relacionados), poderíamos ter usado algo como length++ou length+=1. Infelizmente, nos scripts do vim, +=não pode ser usado imediatamente. Ele precisa ser usado com :setou com :let. Isso significa que :let length+=1incrementa um número, mas não retorna nada. Nós não podemos escrever :s/pattern/\=(length+=1). Precisamos de outra coisa.

Precisamos de funções mutantes. ou seja, funções que modificam suas entradas. Temos setreg(), map(), add()e provavelmente mais. Vamos começar com eles.

  • setreg()muda um registro. Perfeito. Podemos escrever setreg('a',@a+1)como na solução do @Doktor OSwaldo. E, no entanto, isso não é suficiente. setreg()é mais um procedimento do que uma função (para aqueles que conhecem Pascal, Ada ...). Isso significa que não retorna nada. Na verdade, ele retorna algo. Saída nominal (ou seja , saídas não-excepcionais ) sempre retorna algo. Por padrão, quando esquecemos de retornar algo, 0 é retornado - isso também se aplica às funções internas. É por isso que em sua solução a expressão de substituição é realmente \=@a+setreg(...). Complicado, não é?

  • map()também pode ser usado. Se começarmos de uma lista com um único 0 ( :let single_length=[0]), poderemos incrementá-la graças a map(single_length, 'v:val + 1'). Então precisamos retornar o novo comprimento. Ao contrário setreg(), map()retorna sua entrada mutada. Perfeito, o comprimento é armazenado na primeira (e única e, portanto, última) posição da lista. A expressão de substituição pode ser \=map(...)[0].

  • add()é o que eu costumo usar por hábito (eu pensei sobre isso map()na verdade, e ainda não participei das respectivas apresentações). A idéia add()é usar uma lista como o estado atual e acrescentar algo no final antes de cada substituição. Costumo armazenar o novo valor no final da lista e usá-lo para a substituição. Como add()também retorna a sua lista de entrada mutante, podemos usar: \=add(state, Func(state[-1], submatch(0)))[-1]. No caso do OP, precisamos apenas lembrar quantas correspondências foram detectadas até o momento. Retornar o comprimento dessa lista de estados é suficiente. Daí meu \=len(add(state, whatever)).

Luc Hermitte
fonte
Eu acho que entendo este, mas por que o truque com a matriz e seu comprimento comparado a adicionar um a uma variável?
Hippietrail
1
Isso ocorre porque \=espera uma expressão e, ao contrário de C, i+=1não é algo que incrementa e retorna uma expressão. Isso significa que, por trás \=, preciso de algo que possa modificar um contador e que retorne uma expressão (igual a esse contador). Até agora, as únicas coisas que encontrei são as funções de manipulação de lista (e dicionário). O @Doktor OSwaldo usou outra função de mutação ( setreg()). a diferença é que setreg()nunca retorna nada, o que significa que sempre retorna o número 0.
Luc Hermitte
Nossa interessante! Tanto o seu truque como o dele são tão mágicos que acho que suas respostas se beneficiariam de explicá-las em suas respostas. Eu diria que apenas os vimscripters mais fluentes conheceriam expressões idiomáticas.
Hippietrail
2
@hippietrail. Explicações adicionadas. Entre em contato se precisar de precisões mais específicas.
Luc Hermitte
13
 :let @a=1 | %s/search/\='replace'.(@a+setreg('a',@a+1))/g

Mas cuidado, ele substituirá seu registro a. Eu acho que é um pouco mais direto do que a resposta de luc, mas talvez a dele seja mais rápida. Se essa solução for de alguma forma pior que a dele, eu gostaria de ouvir qualquer comentário sobre por que sua resposta é melhor. Qualquer feedback para melhorar a resposta será muito apreciado!

(Também é baseado em uma resposta SO da minha /programming/43539251/how-to-replace-finding-words-with-the-different-in-each-occurrence-in-vi-vim -edi / 43539546 # 43539546 )

Doktor OSwaldo
fonte
Não vejo como @a+setreg('a',@a+1)é mais curto que len(add(t,1)). Caso contrário, este é um outro truque sujo :). Ainda não pensei nisso. Em relação ao uso de uma função de mutação de dicionário no texto de substituição, de :se substitute(), observei que isso é muito mais rápido que os loops explícitos - daí a implementação das funções de minha lista no lh-vim-lib . Eu acho que sua solução será igual à minha, pode ser um pouco mais rápida, eu não sei.
10137 Luc Hermitte
2
Em relação às preferências, prefiro minha solução por um único motivo: ela @anão é modificada. Nos scripts, isso é importante na IMO. Enquanto estiver no modo interativo, como usuário final, saberei qual registro posso usar. Mexer com um registro é menos importante. Na minha solução, no modo interativo, uma variável global é alterada; em um script, seria uma variável local.
Luc Hermitte
@ LucHermitte Desculpe, minha solução não é mais curta que a sua, devo lê-la melhor antes de escrever essa declaração. Tirei a referida declaração da minha resposta e gostaria de me desculpar! Obrigado pelo seu feedback interessante, agradeço.
Doktor Oswaldo
Não se preocupe com isso. Por causa da regex, é fácil pensar que há muito o que digitar. Além disso, eu voluntariamente admito que minha solução está complicada. Você é bem-vindo pelo feedback. :)
Luc Hermitte
1
Na verdade, você é rigoroso. Na maioria das vezes, extraio outra informação que guardo na última posição da matriz, que é o que (o último elemento) insiro no final. Por exemplo, para um +3, eu poderia escrever algo como \=add(thelist, 3 + get(thelist, -1, 0))[-1].
10137 Luc Hermitte
5

Encontrei uma pergunta semelhante, mas diferente, que fiz há alguns anos e consegui alterar uma de suas respostas sem entender completamente o que estava fazendo e tem funcionado muito bem:

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

Especificamente, não entendo por que o meu não usa %ou por que apenas uso uma variável simples que as outras respostas evitam por algum motivo.

hippietrail
fonte
1
isso também é uma possibilidade. Eu acho que a principal desvantagem aqui é que você usa um comando substituto por partida. Portanto, é provavelmente mais lento. A razão pela qual não usamos uma variável simples é que ela não será atualizada em uma s//ginstrução normal . Enfim, é uma solução interessante. Talvez o @LucHermitte possa lhe dizer mais sobre prós e contras, já que meu conhecimento sobre o vimscript é bastante limitado em comparação com o dele.
Doktor Oswaldo
1
@DoktorOSwaldo. Eu acho que essa solução está funcionando há mais tempo - sem, no printf()entanto -, como as Listas foram introduzidas no Vim 7. Mas devo admitir que não esperava (/ não lembrava?) Que <bar>pertencessem ao escopo de :global- IOW, o cenário que eu esperava era aplicar as :sublinhas correspondentes e incrementar iuma vez no final. Espero que esta solução seja um pouco mais lenta. Mas isso realmente importa? O importante é a facilidade com que conseguimos encontrar uma solução funcional da memória + tentativa e erro. Por exemplo, os Vimgolfers preferem macros.
10137 Luc Hermitte
1
@ LucHermitte sim, eu ecoei o mesmo, e não, a velocidade não importa. Eu acho que é uma boa resposta e aprendi novamente algo com isso. Talvez o g/s//comportamento do escopo permita outros truques sujos. Então, obrigado pelas respostas e discussões interessantes, muitas vezes não aprendo muito dando uma resposta =).
Doktor OSwaldo
4

Já existem três ótimas respostas nesta página, mas, como Luc Hermitte sugeriu em um comentário , se você está fazendo isso sem esforço, o importante é a rapidez e facilidade com que você pode encontrar uma solução em funcionamento.

Como tal, este é um problema que eu realmente não usaria :substitute: é um que pode ser facilmente resolvido usando comandos normais do modo normal e uma macro recursiva:

  1. (Se necessário) Primeiro, desligue 'wrapscan'. A expressão regular que usaremos corresponderá ao texto do resultado desejado e ao texto inicial. Assim 'wrapscan', a macro continuaria sendo reproduzida para sempre. (Ou até você perceber o que está acontecendo e pressionar <C-C>.):

    :set nowrapscan
    
  2. Configure seu termo de pesquisa (usando a mesma expressão regular de base já mencionada nas respostas existentes):

    /#\d\+\(\.\d\+\)\?<CR>
    
  3. (Se necessário) Volte para a primeira partida pressionando Nquantas vezes for necessário,

  4. (Se necessário) Altere a primeira correspondência para o texto desejado:

    cE#1<Esc> 
    
  5. Limpe o "qregistro e comece a gravar uma macro:

    qqqqq
    
  6. Arranque o contador atual:

    yiW
    
  7. Ir para a próxima partida:

    n
    
  8. Substitua o contador atual pelo que acabamos de puxar:

    vEp
    
  9. Incremente o contador:

    <C-A>
    
  10. Tocar macro q. O registro "qainda está vazio porque o limpamos na etapa 5; portanto, nada acontece neste momento:

    @q
    
  11. Pare de gravar a macro:

    q
    
  12. Jogue a nova macro e assista!

    @q
    

Como em todas as macros, isso parece muitas etapas quando explicado como fiz acima, mas observe que realmente digitá-las é muito rápido para mim: além do padrão-recursivo-macro-gravação-padrão, eles são todos regulares comandos de edição Estou executando o tempo todo durante a edição. O único passo em que eu tive que fazer qualquer coisa que se aproximasse do pensamento foi o passo 2, onde escrevi a expressão regular para realizar a pesquisa.

Formatada como dois comandos no modo de linha de comando e uma série de pressionamentos de tecla, a velocidade desse tipo de solução fica mais clara: posso conjurar o seguinte com a maior rapidez possível com a digitação 1 :

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

Provavelmente, eu poderia ter pensado nas outras soluções desta página com um pouco de reflexão e algumas referências à documentação 2 , mas, depois de entender como as macros funcionam, elas são fáceis de produzir na velocidade que você costuma editar.

1: Não são situações em que macros requerem mais pensamento, mas acho que eles não vêm-se muito na prática. E geralmente as situações em que ocorrem são aquelas em que uma macro é a única solução prática.

2: Para não implicar que os outros respondentes não poderiam ter encontrado suas soluções da mesma maneira facilmente: eles apenas exigem habilidades / conhecimentos que eu pessoalmente não tenho tão facilmente na ponta dos dedos. Mas todos os usuários do Vim sabem como usar comandos de edição regulares!

Rico
fonte