Làm thế nào để thay thế mỗi trận đấu với bộ đếm tăng dần?


13

Tôi muốn tìm kiếm và thay thế mỗi lần xuất hiện của một mẫu nhất định bằng một số thập phân bắt đầu 1và tăng thêm một cho mỗi trận đấu.

Tôi có thể tìm thấy những câu hỏi tương tự mà hóa ra không phải là về việc tăng một bộ đếm mà sửa đổi mỗi trận đấu bằng một số tiền cố định. Các câu hỏi tương tự khác là về việc chèn số dòng thay vì bộ đếm tăng dần.

Ví dụ, trước:

#1
#1.25
#1.5
#2

Sau:

#1
#2
#3
#4

Dữ liệu thực của tôi có nhiều văn bản hơn xung quanh những thứ tôi muốn đánh số lại.


2
nếu bạn có perldo, bạn có thể sử dụng:%perldo s/#\K\d+(\.\d+)?/++$i/ge
Sundeep

@Sundeep: tôi nên đề cập đến tôi trên vanilla Windows 10, xin lỗi!
hà mã

Câu trả lời:


15

Bạn cần thay thế bằng một nhà nước. Tôi nhớ đã cung cấp một (/ vài?) Giải pháp hoàn chỉnh cho loại vấn đề này trên SO.

Đây là một cách khác để tiến hành (1). Bây giờ, tôi sẽ tiến hành thành 2 bước:

  • một biến danh sách giả tôi cần cho thủ thuật bẩn thỉu và hỗn độn mà tôi sẽ sử dụng
  • một sự thay thế trong đó tôi chèn len của mảng giả này tôi sẽ điền vào mỗi lần xuất hiện khớp.

Cung cấp cho:

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

Nếu bạn chưa quen với vim regexes, tôi sử dụng :h /\zs\zechỉ định mẫu phụ nào tôi phù hợp, thì tôi khớp một loạt các chữ số có thể theo sau là dấu chấm và các chữ số khác. Điều này không hoàn hảo cho bất kỳ số dấu phẩy động nào, nhưng điều này là đủ ở đây.

Lưu ý: Bạn sẽ phải gói nó trong một vài chức năng + lệnh cho một giao diện đơn giản. Một lần nữa, có những ví dụ về SO / vim ( ở đây , ở đây , ở đây ) Ngày nay tôi biết đủ vim để không quan tâm đến việc gói thủ thuật này vào một lệnh. Thật vậy, tôi sẽ có thể viết lệnh này trong lần thử đầu tiên, trong khi tôi sẽ mất vài phút để nhớ tên của lệnh.


(1) Mục tiêu là có thể duy trì trạng thái ở giữa các sự thay thế và thay thế sự xuất hiện hiện tại bằng một điều gì đó phụ thuộc vào trạng thái hiện tại.

Nhờ :s\=chúng tôi có thể chèn một cái gì đó từ một tính toán.

Vẫn là vấn đề của nhà nước. Hoặc chúng tôi xác định một chức năng quản lý trạng thái bên ngoài hoặc chúng tôi tự cập nhật trạng thái bên ngoài. Trong C (và các ngôn ngữ liên quan), chúng ta có thể đã sử dụng một cái gì đó như length++hoặc length+=1. Thật không may, trong các tập lệnh vim, +=không thể được sử dụng ra khỏi hộp. Nó cần phải được sử dụng với :sethoặc với :let. Điều này có nghĩa là :let length+=1tăng một số, nhưng không trả về bất cứ thứ gì. Chúng tôi không thể viết :s/pattern/\=(length+=1). Chúng tôi cần một cái gì đó khác.

Chúng ta cần các chức năng đột biến. tức là các chức năng làm thay đổi đầu vào của chúng. Chúng tôi có setreg(), map(), add()và có lẽ nhiều hơn nữa. Hãy bắt đầu với họ.

  • setreg()đột biến một đăng ký. Hoàn hảo. Chúng ta có thể viết setreg('a',@a+1)như trong giải pháp của @Doktor OSwaldo. Tuy nhiên, điều này là không đủ. setreg()là một thủ tục hơn là một hàm (đối với những người trong chúng ta biết Pascal, Ada ...). Điều này có nghĩa là nó không trả lại bất cứ điều gì. Trên thực tế, nó trả lại một cái gì đó. Lối ra danh nghĩa (tức là lối thoát không ngoại lệ ) luôn trả lại một cái gì đó. Theo mặc định, khi chúng tôi quên trả lại một cái gì đó, 0 được trả về - nó cũng được áp dụng với các hàm tích hợp. Đó là lý do tại sao trong giải pháp của mình, biểu thức thay thế là thực sự \=@a+setreg(...). Tricky, phải không?

  • map()cũng có thể được sử dụng. Nếu chúng tôi bắt đầu từ một danh sách với một 0 ( :let single_length=[0]), chúng tôi có thể tăng nó nhờ map(single_length, 'v:val + 1'). Sau đó, chúng ta cần phải trả lại chiều dài mới. Không giống như setreg(), map()trả về đầu vào đột biến của nó. Thật hoàn hảo, độ dài được lưu trữ ở vị trí đầu tiên (và duy nhất, và do đó cũng kéo dài) của danh sách. Các biểu thức thay thế có thể là \=map(...)[0].

  • add()là thói quen tôi thường sử dụng theo thói quen (thực ra tôi chỉ nghĩ về điều đó map()và tôi chưa chuẩn bị màn trình diễn tương ứng của họ). Ý tưởng với add()là sử dụng một danh sách làm trạng thái hiện tại và nối thêm một cái gì đó vào cuối trước mỗi lần thay thế. Tôi thường lưu trữ giá trị mới ở cuối danh sách và sử dụng nó để thay thế. Cũng như add()trả về danh sách đầu vào đột biến của nó, chúng ta có thể sử dụng : \=add(state, Func(state[-1], submatch(0)))[-1]. Trong trường hợp của OP, chúng ta chỉ cần nhớ có bao nhiêu trận đấu đã được phát hiện cho đến nay. Trả lại độ dài của danh sách nhà nước này là đủ. Do đó của tôi \=len(add(state, whatever)).


Tôi nghĩ rằng tôi hiểu điều này, nhưng tại sao mẹo với mảng và độ dài của nó so với việc thêm một vào một biến?
hà mã

1
Điều này là do \=mong đợi một biểu thức và vì không giống như C, i+=1không phải là thứ gì đó làm tăng trả về một biểu thức. Điều này có nghĩa là phía sau \=tôi cần một cái gì đó có thể sửa đổi một bộ đếm và trả về một biểu thức (bằng với bộ đếm đó). Cho đến nay, điều duy nhất tôi tìm thấy là các hàm thao tác danh sách (và từ điển). @Doktor OSwaldo đã sử dụng một hàm đột biến khác ( setreg()). sự khác biệt là setreg()không bao giờ trả lại bất cứ thứ gì, có nghĩa là nó luôn trả về số 0.
Luc Hermitte

Ồ, thật thú vị! Cả mánh khóe của bạn và anh ta đều kỳ diệu đến nỗi tôi nghĩ câu trả lời của bạn sẽ có lợi từ việc giải thích chúng trong câu trả lời của bạn. Tôi sẽ cho rằng chỉ những người vimscript thông thạo nhất mới biết những thành ngữ không trực quan như vậy.
hà mã

2
@hippietrail. Giải thích thêm. Hãy cho tôi biết nếu bạn cần các biện pháp cụ thể hơn.
Luc Hermitte

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

Nhưng hãy cẩn thận, nó sẽ ghi đè đăng ký của bạn a. Tôi nghĩ rằng nó hơi thẳng hơn một chút so với câu trả lời của luc, nhưng có lẽ câu trả lời của anh ấy nhanh hơn. Nếu giải pháp này bằng cách nào đó tồi tệ hơn anh ta, tôi rất thích nghe bất kỳ phản hồi nào tại sao câu trả lời của anh ta tốt hơn. Bất kỳ thông tin phản hồi để cải thiện câu trả lời sẽ được đánh giá cao!

(Nó cũng dựa trên câu trả lời SO của tôi /programming/43539251/how-to-replace-finding-words-with-the-different-in-each-occurrence-in-vi-vim -edi / 43539546 # 43539546 )


Tôi không thấy làm thế nào @a+setreg('a',@a+1)ngắn hơn len(add(t,1)). Nếu không, đây là một mẹo bẩn tốt đẹp khác :). Tôi đã không mặc dù điều này. Về việc sử dụng chức năng biến đổi từ điển trong văn bản thay thế, :ssubstitute(), tôi đã lưu ý rằng điều này nhanh hơn nhiều so với các vòng lặp rõ ràng - do đó việc thực hiện các chức năng danh sách của tôi trong lh-vim-lib . Tôi đoán giải pháp của bạn sẽ ngang bằng với tôi, có thể nhanh hơn một chút, tôi không biết.
Luc Hermitte

2
Về sở thích, tôi thích giải pháp của tôi vì một lý do duy nhất: nó @akhông thay đổi. Trong các kịch bản, đây là IMO quan trọng. Khi ở chế độ tương tác, với tư cách là người dùng cuối, tôi sẽ biết mình có thể sử dụng đăng ký nào. Lộn xộn với một đăng ký là ít quan trọng. Trong giải pháp của tôi, trong chế độ tương tác, một biến toàn cục bị rối tung; trong một kịch bản, nó sẽ là một biến cục bộ.
Luc Hermitte

@LucHermitte Xin lỗi, giải pháp của tôi thực sự không ngắn hơn của bạn, tôi nên đọc nó tốt hơn trước khi viết một tuyên bố như vậy. Tôi đã xóa tuyên bố nói khỏi câu trả lời của tôi và muốn xin lỗi! Cảm ơn bạn đã phản hồi thú vị của bạn, tôi đánh giá cao nó.
Doktor OSwaldo

Đừng lo lắng về nó. Bởi vì regex, thật dễ dàng để nghĩ rằng có rất nhiều loại. Ngoài ra, tôi tự nguyện thừa nhận giải pháp của tôi là khó khăn. Bạn được hoan nghênh phản hồi. :)
Luc Hermitte

1
Quả thực bạn là người cứng nhắc. Hầu hết thời gian tôi trích xuất một thông tin khác mà tôi lưu trữ ở vị trí cuối cùng của mảng, đó là những gì (phần tử cuối cùng) tôi chèn vào cuối. Ví dụ, đối với a +3, tôi có thể viết một cái gì đó như \=add(thelist, 3 + get(thelist, -1, 0))[-1].
Luc Hermitte

5

Tôi đã tìm thấy một câu hỏi tương tự nhưng khác nhau mà tôi đã hỏi vài năm trước và đã thay đổi một trong những câu trả lời của nó mà không hiểu đầy đủ những gì tôi đang làm và nó đang hoạt động rất tốt:

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

Cụ thể tôi không hiểu tại sao tôi không sử dụng %hoặc tại sao tôi chỉ sử dụng một biến đơn giản mà các câu trả lời khác tránh vì một số lý do.


1
đó cũng là một khả năng Tôi nghĩ nhược điểm chính ở đây là, bạn sử dụng một lệnh thay thế cho mỗi trận đấu. Vì vậy, nó có thể là chậm hơn. Lý do tại sao chúng ta không sử dụng một biến đơn giản là nó sẽ không được cập nhật trong một s//gtuyên bố bình thường . Dù sao nó là một giải pháp thú vị. Có lẽ @LucHermitte có thể cho bạn biết nhiều hơn về ưu và nhược điểm, vì kiến ​​thức của tôi về vimscript khá hạn chế so với của anh ấy.
Doktor OSwaldo

1
@DoktorOSwaldo. Tôi đoán giải pháp này đã hoạt động trong một thời gian dài hơn - printf()mặc dù vậy - như Danh sách đã được giới thiệu trong Vim 7. Nhưng tôi phải thừa nhận rằng tôi sẽ không mong đợi (/ không nhớ?) <bar>Thuộc về phạm vi của :global- IOW, kịch bản tôi mong đợi là áp dụng các :subdòng khớp, sau đó tăng imột lần vào cuối. Tôi hy vọng giải pháp này sẽ chậm hơn một chút. Nhưng nó thực sự quan trọng? Điều quan trọng là chúng ta có thể dễ dàng tìm ra giải pháp hoạt động như thế nào từ bộ nhớ + dùng thử & lỗi. Chẳng hạn, Vimgolfers thích macro.
Luc Hermitte

1
@LucHermitte vâng tôi cũng hiểu như vậy, và tốc độ không thành vấn đề. Tôi nghĩ rằng đó là một câu trả lời tốt, và tôi lại học được điều gì đó từ nó. Có thể g/s//hành vi phạm vi cho phép cho các thủ đoạn bẩn thỉu khác. Vì vậy, cảm ơn cả hai vì những câu trả lời và thảo luận thú vị, tôi không thường học được nhiều từ việc đưa ra câu trả lời =).
Doktor OSwaldo

4

Đã có ba câu trả lời tuyệt vời trên trang này, nhưng, như Luc Hermitte đã đề xuất trong một bình luận , nếu bạn đang thực hiện điều này, điều quan trọng là bạn có thể nhanh chóng và dễ dàng tìm ra giải pháp làm việc nhanh chóng và dễ dàng như thế nào.

Như vậy, đây là một vấn đề mà tôi thực sự không sử dụng :substitutecho tất cả: đó là một vấn đề có thể dễ dàng giải quyết bằng các lệnh chế độ thông thường và macro đệ quy:

  1. (Nếu cần) Đầu tiên, tắt 'wrapscan'. Biểu thức chính quy mà chúng tôi sẽ sử dụng sẽ khớp với văn bản kết quả mong muốn cũng như văn bản ban đầu, do đó 'wrapscan', trên macro, nếu không, macro sẽ tiếp tục phát lại mãi mãi. (Hoặc cho đến khi bạn nhận ra những gì đang xảy ra và nhấn <C-C>.):

    :set nowrapscan
    
  2. Thiết lập thuật ngữ tìm kiếm của bạn (sử dụng cùng một biểu thức chính quy cơ bản đã được đề cập trong các câu trả lời hiện có):

    /#\d\+\(\.\d\+\)\?<CR>
    
  3. (Nếu cần) Quay trở lại trận đấu đầu tiên bằng cách nhấn Nnhiều lần theo yêu cầu,

  4. (Nếu cần) Thay đổi kết quả khớp đầu tiên thành văn bản mong muốn:

    cE#1<Esc> 
    
  5. Xóa "qsổ đăng ký và bắt đầu ghi macro:

    qqqqq
    
  6. Yank quầy hiện tại:

    yiW
    
  7. Chuyển sang trận đấu tiếp theo:

    n
    
  8. Thay thế bộ đếm hiện tại bằng bộ đếm chúng ta vừa kéo:

    vEp
    
  9. Tăng số lượt truy cập:

    <C-A>
    
  10. Chơi macro q. Đăng ký "qvẫn trống vì chúng tôi đã xóa nó trong bước 5, vì vậy không có gì xảy ra tại thời điểm này:

    @q
    
  11. Dừng ghi macro:

    q
    
  12. Chơi macro mới và xem!

    @q
    

Như với tất cả các macro, điều này trông giống như rất nhiều bước khi được giải thích như tôi đã làm ở trên, nhưng lưu ý rằng thực sự nhập chúng vào rất nhanh: ngoài việc ghi lại macro-đệ quy-macro lệnh chỉnh sửa Tôi đang thực hiện tất cả các thời gian trong khi chỉnh sửa. Bước duy nhất mà tôi phải làm bất cứ điều gì ngay cả khi tiếp cận suy nghĩ là bước 2, nơi tôi đã viết biểu thức chính quy để thực hiện tìm kiếm.

Được định dạng là hai lệnh chế độ dòng lệnh và một loạt các lần nhấn phím, tốc độ của loại giải pháp này trở nên rõ ràng hơn: Tôi có thể gợi ý những điều sau đây nhanh nhất có thể bằng cách gõ 1 :

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

Tôi có thể đã đưa ra các giải pháp khác trên trang này với một chút suy nghĩ và một số tài liệu tham khảo của tài liệu 2 , nhưng, khi bạn hiểu cách thức hoạt động của macro, chúng thực sự dễ dàng đưa ra bất cứ tốc độ nào bạn thường chỉnh sửa.

1: Có những tình huống mà macro đòi hỏi nhiều suy nghĩ hơn, nhưng tôi thấy chúng không xuất hiện nhiều trong thực tế. Và nói chung, các tình huống xảy ra là những tình huống mà macro là giải pháp thực tế duy nhất .

2: Không ngụ ý rằng những người trả lời khác không thể đưa ra giải pháp tương tự một cách dễ dàng: họ chỉ yêu cầu những kỹ năng / kiến ​​thức mà cá nhân tôi không dễ dàng trong tầm tay. Nhưng tất cả người dùng Vim đều biết cách sử dụng các lệnh chỉnh sửa thông thường!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.