Bagaimana cara mengganti setiap pertandingan dengan penghitung yang bertambah?

14

Saya ingin mencari dan mengganti setiap kemunculan pola tertentu dengan angka desimal yang dimulai dengan 1dan bertambah satu untuk setiap kecocokan.

Saya dapat menemukan pertanyaan dengan kata yang sama yang ternyata bukan tentang menambah penghitung tetapi memodifikasi setiap kecocokan dengan jumlah yang tetap. Pertanyaan serupa lainnya adalah tentang memasukkan nomor baris dan bukan penghitung yang bertambah.

Contoh, sebelum:

#1
#1.25
#1.5
#2

Setelah:

#1
#2
#3
#4

Data saya yang sebenarnya memiliki lebih banyak teks di sekitar hal-hal yang saya ingin nomor ulang.

hippietrail
sumber
2
jika sudah perldo, Anda dapat menggunakan:%perldo s/#\K\d+(\.\d+)?/++$i/ge
Sundeep
@Sundeep: saya seharusnya menyebutkan saya menggunakan vanilla Windows 10, maaf!
hippietrail

Jawaban:

15

Anda perlu substitusi dengan negara. Saya ingat pernah memberikan (/ beberapa?) Solusi lengkap untuk masalah seperti ini pada SO.

Berikut ini cara lain untuk melanjutkan (1). Sekarang, saya akan melanjutkan ke 2 langkah:

  • variabel dummy list yang saya butuhkan untuk trik kotor dan berbelit-belit yang akan saya pakai
  • substitusi tempat saya memasukkan len array dummy ini yang saya isi pada setiap kejadian yang cocok.

Pemberian yang mana:

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

Jika Anda tidak terbiasa dengan regex, saya menggunakan :h /\zsdan \zeuntuk menentukan sub-pola mana yang saya cocok, maka saya cocok dengan serangkaian digit yang mungkin diikuti oleh titik dan angka lainnya. Ini tidak sempurna untuk angka floating point, tetapi ini sudah cukup di sini.

Catatan: Anda harus membungkusnya dalam perintah + fungsi pasangan untuk antarmuka yang sederhana. Sekali lagi, ada contoh pada SO / vim (di sini , di sini , di sini ) Saat ini saya cukup tahu vim untuk tidak peduli tentang membungkus trik ini menjadi sebuah perintah. Memang saya akan dapat menulis perintah ini pada percobaan pertama, sementara saya akan mengambil beberapa menit untuk mengingat nama perintah.


(1) Tujuannya adalah untuk dapat mempertahankan keadaan di antara pergantian, dan untuk mengganti kejadian saat ini dengan sesuatu yang tergantung pada keadaan saat ini.

Berkat :s\=kami dapat menyisipkan sesuatu yang dihasilkan dari perhitungan.

Tetap masalah negara. Baik kita mendefinisikan fungsi yang mengelola keadaan eksternal, atau kita memperbarui diri kita sendiri keadaan eksternal. Dalam C (dan bahasa terkait), kami bisa menggunakan sesuatu seperti length++atau length+=1. Sayangnya, dalam skrip vim, +=tidak dapat digunakan di luar kotak. Itu perlu digunakan dengan :setatau dengan :let. Ini berarti :let length+=1menambah angka, tetapi tidak mengembalikan apa pun. Kami tidak bisa menulis :s/pattern/\=(length+=1). Kami membutuhkan sesuatu yang lain.

Kami membutuhkan fungsi bermutasi. yaitu fungsi yang mengubah inputnya. Kami memiliki setreg(), map(), add()dan mungkin lebih. Mari kita mulai dengan mereka.

  • setreg()bermutasi register. Sempurna. Kita bisa menulis setreg('a',@a+1)seperti pada solusi @Doktor OSwaldo. Namun, ini tidak cukup. setreg()lebih merupakan prosedur daripada fungsi (bagi mereka yang tahu Pascal, Ada ...). Ini berarti tidak mengembalikan apa pun. Sebenarnya, itu mengembalikan sesuatu. Nominal exit (yaitu pintu keluar non-pengecualian ) selalu mengembalikan sesuatu. Secara default, ketika kami lupa mengembalikan sesuatu, 0 dikembalikan - ini juga berlaku dengan fungsi bawaan . Itu sebabnya dalam solusinya ekspresi pengganti sebenarnya \=@a+setreg(...). Tricky, bukan?

  • map()bisa juga digunakan. Jika kita mulai dari daftar dengan satu 0 ( :let single_length=[0]), kita bisa menambahkannya berkat map(single_length, 'v:val + 1'). Maka kita perlu mengembalikan panjang baru. Tidak seperti setreg(), map()mengembalikan input yang dimutasi. Itu sempurna, panjangnya disimpan pada posisi pertama (dan unik, dan juga terakhir) dari daftar. Ekspresi pengganti bisa \=map(...)[0].

  • add()adalah salah satu yang sering saya gunakan karena kebiasaan (saya baru saja map()benar - benar tahu, dan saya belum menentukan penampilan masing-masing). Gagasannya add()adalah menggunakan daftar sebagai kondisi saat ini, dan menambahkan sesuatu di akhir sebelum setiap penggantian. Saya sering menyimpan nilai baru di akhir daftar, dan menggunakannya untuk penggantian. Seperti add()juga kembali daftar masukan bermutasi, kita bisa menggunakan: \=add(state, Func(state[-1], submatch(0)))[-1]. Dalam kasus OP, kita hanya perlu mengingat berapa banyak kecocokan yang terdeteksi sejauh ini. Mengembalikan panjang daftar negara ini sudah cukup. Karena itu saya \=len(add(state, whatever)).

Luc Hermitte
sumber
Saya rasa saya mengerti yang ini, tapi mengapa trik dengan array dan panjangnya dibandingkan dengan menambahkan satu ke variabel?
hippietrail
1
Ini karena \=mengharapkan ekspresi, dan karena tidak seperti C, i+=1bukanlah sesuatu yang melakukan peningkatan dan mengembalikan ekspresi. Ini berarti bahwa di belakang \=saya memerlukan sesuatu yang dapat memodifikasi penghitung dan yang mengembalikan ekspresi (sama dengan penghitung itu). Sejauh ini, satu-satunya hal yang saya temukan adalah fungsi manipulasi daftar (dan kamus). @Doktor OSwaldo telah menggunakan fungsi bermutasi lainnya ( setreg()). perbedaannya adalah bahwa setreg()tidak pernah mengembalikan apa pun, yang berarti selalu mengembalikan nomornya 0.
Luc Hermitte
Wow, menarik! Trik Anda dan triknya sangat ajaib sehingga saya pikir jawaban Anda akan mendapat manfaat dengan menjelaskannya dalam jawaban Anda. Saya akan berasumsi hanya penulis naskah yang paling fasih yang akan mengetahui idiom yang tidak intuitif ini.
hippietrail
2
@hippietrail. Penjelasan ditambahkan. Beri tahu saya jika Anda membutuhkan lebih banyak persiapan khusus.
Luc Hermitte
13
 :let @a=1 | %s/search/\='replace'.(@a+setreg('a',@a+1))/g

Namun berhati-hatilah, itu akan menimpa register Anda a. Saya pikir ini sedikit lebih lurus daripada jawaban luc, tapi mungkin itu lebih cepat. Jika solusi ini entah bagaimana lebih buruk daripada nya, saya akan senang mendengar umpan balik mengapa jawabannya lebih baik. Umpan balik apa pun untuk meningkatkan jawaban akan sangat dihargai!

(Ini juga didasarkan pada jawaban SO saya /programming/43539251/how-to-replace-finding-words-with-the-different-in-each-occurrence-in-vi-vim -edi / 43539546 # 43539546 )

Doktor OSwaldo
sumber
Saya tidak melihat bagaimana @a+setreg('a',@a+1)lebih pendek dari len(add(t,1)). Kalau tidak, ini adalah trik kotor lain yang bagus :). Saya belum memikirkan yang satu ini. Mengenai penggunaan fungsi kamus yang mutasi dalam teks pengganti, dari :sdan substitute(), saya telah mencatat ini jauh lebih cepat dari loop eksplisit - maka implementasi fungsi daftar saya di lh-vim-lib . Saya kira solusi Anda akan setara dengan saya, mungkin sedikit lebih cepat, saya tidak tahu.
Luc Hermitte
2
Mengenai preferensi, saya lebih suka solusi saya karena satu alasan: ia meninggalkan @atidak dimodifikasi. Dalam skrip, ini adalah IMO penting. Saat dalam mode interaktif, sebagai pengguna akhir, saya akan tahu register mana yang dapat saya gunakan. Messing dengan register kurang penting. Dalam solusi saya, dalam mode interaktif, variabel global kacau; dalam skrip akan menjadi variabel lokal.
Luc Hermitte
@LucHermitte Maaf, solusi saya memang tidak lebih pendek dari milik Anda, saya harus membacanya lebih baik sebelum menulis pernyataan seperti itu. Saya telah menghapus pernyataan tersebut dari jawaban saya dan ingin meminta maaf! Terima kasih atas tanggapan Anda yang menarik, saya menghargainya.
Doktor OSwaldo
Jangan khawatir tentang itu. Karena regex, mudah untuk berpikir ada banyak untuk diketik. Juga, saya secara sukarela mengakui solusi saya berbelit-belit. Terima kasih atas umpan baliknya. :)
Luc Hermitte
1
Memang Anda tegar. Sebagian besar waktu saya mengekstrak informasi lain yang saya simpan di posisi terakhir array, yaitu apa (elemen terakhir) yang saya masukkan di akhir. Misalnya, untuk a +3, saya bisa menulis sesuatu seperti \=add(thelist, 3 + get(thelist, -1, 0))[-1].
Luc Hermitte
5

Saya menemukan pertanyaan serupa tetapi berbeda yang saya tanyakan beberapa tahun yang lalu dan berhasil mengubah salah satu jawabannya tanpa sepenuhnya memahami apa yang saya lakukan dan itu berhasil:

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

Khususnya saya tidak mengerti mengapa saya tidak menggunakan %atau mengapa saya hanya menggunakan variabel polos yang jawaban lain hindari karena alasan tertentu.

hippietrail
sumber
1
itu juga kemungkinan. Saya pikir kerugian utama di sini adalah, bahwa Anda menggunakan satu perintah pengganti per pertandingan. Jadi mungkin lebih lambat. Alasan mengapa kita tidak menggunakan variabel polos, adalah, bahwa itu tidak akan diperbarui dalam s//gpernyataan normal . Pokoknya itu solusi yang menarik. Mungkin @LucHermitte dapat memberi tahu Anda lebih banyak tentang pro dan kontra, karena pengetahuan saya tentang vimscript sangat terbatas dibandingkan dengan itu.
Doktor OSwaldo
1
@DoktorOSwaldo. Saya kira solusi ini telah bekerja untuk waktu yang lebih lama - tanpa printf()sekalipun - karena Daftar diperkenalkan di Vim 7. Tapi saya harus mengakui bahwa saya tidak akan mengharapkan (/ tidak ingat?) <bar>Milik ke dalam lingkup :global- TKI, skenario yang saya harapkan adalah menerapkannya :subpada baris yang cocok, lalu bertambah isatu kali di akhir. Saya berharap solusi ini sedikit lebih lambat. Tetapi apakah itu benar-benar penting? Yang penting adalah seberapa mudah kita bisa datang dengan solusi yang berfungsi dari memori + coba & kesalahan. Misalnya, Vimgolfers lebih suka makro.
Luc Hermitte
1
@LucHermitte ya saya ecpexted sama, dan tidak ada kecepatan tidak masalah. Saya pikir itu adalah jawaban yang baik, dan saya belajar lagi sesuatu darinya. Mungkin g/s//perilaku lingkup memungkinkan untuk trik kotor lainnya. Jadi terima kasih atas jawaban dan diskusi yang menarik, saya tidak sering belajar sebanyak itu dari memberikan jawaban =).
Doktor OSwaldo
4

Sudah ada tiga jawaban bagus di halaman ini, tetapi, seperti yang disarankan Luc Hermitte dalam komentar , jika Anda melakukan ini, yang penting adalah seberapa cepat dan mudah Anda dapat turun pada solusi yang berfungsi.

Dengan demikian, ini adalah masalah yang sebenarnya tidak akan saya gunakan :substitutesama sekali: ini adalah masalah yang dapat dengan mudah dipecahkan menggunakan perintah mode normal biasa dan makro rekursif:

  1. (Jika perlu) Pertama, matikan 'wrapscan'. Ekspresi reguler yang akan kita gunakan akan cocok dengan teks hasil yang diinginkan serta teks awal, jadi dengan 'wrapscan'on, makro akan terus diputar ulang selamanya. (Atau sampai Anda menyadari apa yang terjadi dan tekan <C-C>.):

    :set nowrapscan
    
  2. Siapkan istilah pencarian Anda (menggunakan basis ekspresi reguler yang sama yang telah disebutkan dalam jawaban yang ada):

    /#\d\+\(\.\d\+\)\?<CR>
    
  3. (Jika perlu) Melompat kembali ke pertandingan pertama dengan menekan Nsebanyak yang diperlukan,

  4. (Jika perlu) Ubah kecocokan pertama menjadi teks yang diinginkan:

    cE#1<Esc> 
    
  5. Kosongkan "qregister dan mulai merekam makro:

    qqqqq
    
  6. Tarik penghitung saat ini:

    yiW
    
  7. Lompat ke pertandingan berikutnya:

    n
    
  8. Ganti penghitung saat ini dengan yang baru saja kita tarik:

    vEp
    
  9. Tambahkan penghitung:

    <C-A>
    
  10. Mainkan makro q. Daftar "qmasih kosong karena kami membersihkannya di langkah 5, jadi tidak ada yang terjadi pada saat ini:

    @q
    
  11. Hentikan merekam makro:

    q
    
  12. Mainkan makro baru, dan tonton!

    @q
    

Seperti halnya semua makro, ini terlihat seperti banyak langkah ketika dijelaskan seperti yang telah saya lakukan di atas, tetapi perhatikan bahwa sebenarnya mengetik ini sangat cepat bagi saya: selain dari rekursif-makro-rekaman-boilerplate mereka semua hanya biasa perintah pengeditan yang saya lakukan sepanjang waktu selama pengeditan. Satu-satunya langkah di mana saya harus melakukan apa pun bahkan mendekati pemikiran adalah langkah 2, di mana saya menulis ekspresi reguler untuk melakukan pencarian.

Diformat sebagai dua perintah mode baris perintah dan serangkaian penekanan tombol, kecepatan jenis solusi ini menjadi lebih jelas: Saya dapat menyulap berikut ini secepat saya bisa mengetiknya 1 :

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

Saya mungkin bisa menemukan solusi lain pada halaman ini dengan sedikit pemikiran dan beberapa referensi dari dokumentasi 2 , tetapi, setelah Anda memahami bagaimana macro bekerja, mereka benar-benar mudah untuk menghasilkan kecepatan apa pun yang biasanya Anda edit.

1: Ada yang situasi di mana makro memerlukan lebih banyak pemikiran, tapi saya merasa mereka tidak datang banyak dalam praktek. Dan umumnya situasi di mana mereka terjadi adalah situasi di mana makro adalah satu - satunya solusi praktis.

2: Tidak menyiratkan bahwa penjawab lainnya tidak dapat dengan mudah menemukan solusi mereka: mereka hanya membutuhkan keterampilan / pengetahuan yang secara pribadi tidak saya miliki dengan mudah di ujung jari saya. Tetapi semua pengguna Vim tahu cara menggunakan perintah pengeditan reguler!

Kaya
sumber