Як замінити кожен матч на збільшення лічильника?


14

Я хочу шукати і замінювати кожне виникнення певного шаблону десятковим числом, яке починається з 1та з кроком на одиницю на кожен матч.

Я можу знайти подібні сформульовані питання, які виявляються не про збільшення лічильника, а про зміну кожної відповідності на фіксовану суму. Інші подібні запитання стосуються вставки номерів рядків, а не збільшення лічильника.

Приклад, перед:

#1
#1.25
#1.5
#2

Після:

#1
#2
#3
#4

У моїх реальних даних є набагато більше тексту навколо речей, які я хочу перерахувати.


2
якщо є perldo, можете скористатися:%perldo s/#\K\d+(\.\d+)?/++$i/ge
Sundeep

@ Sundeep: я повинен був згадати, що я на ванільній Windows 10, вибачте!
hippietrail

Відповіді:


15

Вам потрібна підміна державою. Я пам’ятаю, що запропонував (/ кілька?) Повне рішення для подібних проблем на SO.

Ось ще один спосіб продовжити (1). Тепер я перейду до двох етапів:

  • мінливий список змінної, який мені потрібен для брудного і перекрученого трюку, який я буду використовувати
  • заміна, де я вставляю ділянку цього макетного масиву, який я заповнюю на кожну зустрічну відповідність.

Що дає:

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

Якщо ви не використовуєте vim реджекси, я використовую :h /\zsі \zeвказую, який піддіапазон я збігаю, то я співпадаю з серією цифр, можливо, з наступною крапкою та іншими цифрами. Це не ідеально для жодного числа з плаваючою комою, але цього тут достатньо.

Примітка: для простого інтерфейсу вам доведеться перетворити його в пару функцій + команда. Знову є приклади на SO / vim ( тут , тут , тут ). Сьогодні я знаю достатньо vim, щоб не перейматися тим, як вписати цей трюк у команду. Дійсно, я зможу написати цю команду з першої спроби, тоді як мені знадобиться кілька хвилин, щоб запам'ятати назву команди.


(1) Мета полягає в тому, щоб мати можливість підтримувати стан між замінами і замінювати поточне явище чимось, що залежить від поточного стану.

Завдяки цьому :s\=ми можемо вставити щось, що є результатом обчислень.

Залишається проблема держави. Або ми визначаємо функцію, яка керує зовнішнім станом, або ми оновлюємо зовнішній стан. У мові C (і суміжних мовах) ми могли б використовувати щось на зразок length++або length+=1. На жаль, сценарії vim +=не можуть бути використані поза вікном. Її потрібно використовувати або з, :setабо з :let. Це означає, що :let length+=1збільшує число, але нічого не повертає. Ми не можемо писати :s/pattern/\=(length+=1). Нам потрібно щось інше.

Нам потрібні мутаційні функції. тобто функції, які мутують їхні входи. У нас є setreg(), map(), add()і , ймовірно , більше. Почнемо з них.

  • setreg()мутує реєстр. Ідеально. Ми можемо писати setreg('a',@a+1)як у рішенні @Doktor OSwaldo. І все-таки цього недостатньо. setreg()- це скоріше процедура, ніж функція (для тих, хто знає Паскаля, Ада ...). Це означає, що він нічого не повертає. Насправді це щось повертає. Номінальний вихід (тобто невиключні виходи) завжди щось повертає. За замовчуванням, коли ми забули щось повернути, повертається 0 - це також стосується вбудованих функцій. Ось чому в його рішенні насправді вираз заміни \=@a+setreg(...). Хитрий, чи не так?

  • map()також може бути використаний. Якщо ми починаємо зі списку з одним 0 ( :let single_length=[0]), ми можемо збільшити його завдяки map(single_length, 'v:val + 1'). Тоді нам потрібно повернути нову довжину. На відміну від цього setreg(), map()повертає його мутований вхід. Це ідеально, довжина зберігається на першому (і унікальному, і таким чином, в останньому) місці списку. Вираз заміни може бути \=map(...)[0].

  • add()це те, що я часто використовую за звичкою (я хоч і map()насправді, і я ще не брав участь у відповідних виступах). Ідея з add()- використовувати список як поточний стан і додавати щось наприкінці перед кожною підміною. Я часто зберігаю нове значення в кінці списку і використовую його для заміни. Як add()і повертає його мутантний список введення, ми можемо використовувати: \=add(state, Func(state[-1], submatch(0)))[-1]. У випадку з ОП нам потрібно лише пам’ятати, скільки матчів було виявлено до цього часу. Повернення довжини цього списку станів достатньо. Звідси мій \=len(add(state, whatever)).


Я думаю, я це розумію, але чому трюк з масивом та його довжина порівняно з додаванням його до змінної?
hippietrail

1
Це тому, що \=очікує вираз, і тому, що на відміну від C, i+=1це не те, що зростає і повертає вираз. Це означає, що позаду \=мені потрібно щось, що може змінити лічильник і що поверне вираз (рівний цьому лічильнику). Поки що єдине, що я знайшов, - це функції маніпуляції зі списком (та словником). @Doktor OSwaldo використав ще одну функцію, що мутує ( setreg()). Різниця полягає в тому, що setreg()ніколи нічого не повертає, а значить, завжди повертає число 0.
Люк Ермітт

Вау, цікаво! І ваш трюк, і його настільки магічні, що я думаю, що ваші відповіді отримали б користь від пояснення їх у ваших відповідях. Я б припустив, що лише найбідніші вимскриптери знають такі неінтуїтивні ідіоми.
hippietrail

2
@hippietrail. Пояснення додано. Повідомте, чи потрібні більш конкретні заходи.
Люк Ермітт

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

Але будьте обережні, він замінить ваш реєстр a. Я думаю, що це трохи більш прямо, ніж відповідь Люка, але, можливо, його швидше. Якщо це рішення якось гірше його, я б хотів почути будь-який відгук, чому його відповідь краща. Будь-які відгуки для покращення відповіді будуть високо оцінені!

(Він також ґрунтується на моїй відповіді SO /programming/43539251/how-to-replace-finding-words-with-the-different-in-each-occurrence-in-vi-vim -edi / 43539546 # 43539546 )


Я не бачу, як @a+setreg('a',@a+1)коротше, ніж len(add(t,1)). Інакше це приємний інший брудний трюк :). Я не хоч цього. Що стосується використання словника функції мутаційного в тексті заміни, з :sі substitute(), я помітив це набагато швидше, ніж явні цикли - звідси реалізація мого списку функцій в LH-ВІМ-Lib . Я думаю, що ваше рішення буде нарівні з моїм, можливо, трохи швидше, я не знаю.
Люк Ермітт

2
Щодо уподобань, я віддаю перевагу своєму рішенню з єдиної причини: воно залишається @aнезмінним. У сценаріях це важливо ІМО. Перебуваючи в інтерактивному режимі, як кінцевий користувач я буду знати, який реєстр я можу використовувати. Возитися з реєстром менш важливо. У моєму рішенні в інтерактивному режимі глобальна змінна змішана; у сценарії це буде локальна змінна.
Люк Ермітт

@LucHermitte Вибачте, моє рішення дійсно не коротше вашого, я повинен прочитати його краще, перш ніж писати таку заяву. Я вилучив із своєї відповіді сказане твердження і хочу вибачитися! Дякую за ваш цікавий відгук, я вдячний.
Doktor OSwaldo

Не хвилюйся з цього приводу. Через регулярний вираз легко подумати, що можна багато набрати. Крім того, я добровільно визнаю, що моє рішення є суперечливим. Вас чекають відгуки. :)
Люк Ермітт

1
Дійсно, ти ріг. Більшу частину часу я дістаю іншу інформацію, яку я зберігаю в останньому положенні масиву, і саме це (останній елемент) я вставляю в кінці. Наприклад, +3я можу написати щось подібне \=add(thelist, 3 + get(thelist, -1, 0))[-1].
Люк Ермітт

5

Я знайшов подібне, але інше питання, яке я задав пару років тому, і мені вдалося змінити одну з його відповідей, не розуміючи повністю, що я роблю, і це працює чудово:

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

Зокрема, я не розумію, чому мій не використовується %або чому я просто використовую звичайну змінну, якої інші відповіді чомусь уникають.


1
це також можливість. Я думаю, що головним недоліком тут є те, що ви використовуєте одну команду заміну на матч. Тож, мабуть, повільніше. Причина, чому ми не використовуємо звичайну змінну, полягає в тому, що вона не буде оновлена ​​в звичайному s//gоператорі. У будь-якому випадку це цікаве рішення. Можливо, @LucHermitte може розповісти вам більше про плюси і мінуси, оскільки мої знання про vimscript досить обмежені порівняно з його.
Doktor OSwaldo

1
@DoktorOSwaldo. Я думаю, що це рішення працює довше - printf()незважаючи на це, - як списки були представлені у Vim 7. Але я мушу визнати, що я не очікував (би не пам’ятав?) <bar>Належність до сфери :global- IOW, сценарій, який я б очікував, - це застосувати :subвідповідні лінії, а потім приріст iодин раз у самому кінці. Я очікую, що це рішення буде дещо повільнішим. Але чи насправді це має значення? Важливим є те, наскільки легко ми можемо підійти до робочого рішення з пам'яті + проб і помилок. Наприклад, Vimgolfers віддають перевагу макросам.
Люк Ермітт

1
@LucHermitte Так, я так само випереджав, і швидкість не має значення. Я думаю, що це хороша відповідь, і я знову щось з цього навчився. Можливо, g/s//поведінка сфери дозволяє інші брудні хитрощі. Тож дякую вам і за цікаві відповіді та обговорення, я не часто так багато вчуся, як давати відповідь =).
Doktor OSwaldo

4

На цій сторінці вже є три чудові відповіді , але, як в коментарі запропонував Люк Ермітт , якщо ви робите це з манжети, важливим є те, наскільки швидко і легко ви зможете виправити робоче рішення.

Таким чином, це проблема, яку я насправді не використовував би :substituteвзагалі: це проблема, яку легко вирішити за допомогою команд звичайного режиму та рекурсивного макросу:

  1. (При необхідності) Спочатку вимкніть 'wrapscan'. Регулярний вираз, який ми будемо використовувати, буде відповідати бажаному тексту результату, а також початковому тексту, так що з 'wrapscan', макрос інакше буде продовжувати відтворюватися назавжди. (Або поки ви не зрозумієте, що відбувається, і натисніть <C-C>.):

    :set nowrapscan
    
  2. Налаштуйте пошуковий термін (використовуючи той самий базовий регулярний вираз, який уже згадувався в існуючих відповідях):

    /#\d\+\(\.\d\+\)\?<CR>
    
  3. (При необхідності) Перейти до першого матчу, натискаючи Nстільки разів, скільки потрібно,

  4. (При необхідності) Змініть першу відповідність на потрібний текст:

    cE#1<Esc> 
    
  5. Очистіть "qрегістр і починайте записувати макрос:

    qqqqq
    
  6. Виберіть поточний лічильник:

    yiW
    
  7. Перейти до наступного матчу:

    n
    
  8. Замініть поточний лічильник на той, який ми щойно потягнули:

    vEp
    
  9. Збільшення лічильника:

    <C-A>
    
  10. Відтворити макрос q. Реєстрація "qвсе ще порожня, оскільки ми її очистили на кроці 5, тому в цей момент нічого не відбувається:

    @q
    
  11. Зупинити запис макросу:

    q
    
  12. Грайте в новий макрос і дивіться!

    @q
    

Як і у всіх макросах, це виглядає як багато кроків, коли пояснюється, як я вже робив вище, але зауважте, що насправді введення цих текстів для мене дуже швидке: крім рекурсивної макро-записувальної коробки, вони все просто звичайні редагування команд, які я виконую весь час під час редагування. Єдиним кроком, коли мені довелося зробити що-небудь, навіть наближаючись до мислення, був крок 2, де я написав регулярний вираз для здійснення пошуку.

Відформатована як дві команди режиму командного рядка та серія натискань клавіш, швидкість цього типу рішення стає більш зрозумілою: я можу створити наступне досить швидко, як я можу набрати його 1 :

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

Можливо, я міг би придумати інші рішення на цій сторінці, трохи подумавши та посилаючись на документацію 2 , але, як тільки ти зрозумієш, як працюють макроси, їх дійсно легко вимкнути з будь-якою швидкістю, яку ти зазвичай редагуєш.

1: Там є ситуації , коли макроси вимагають більше думки, але я вважаю , що вони не придумали багато на практиці. І взагалі ситуації, коли вони трапляються, - це такі, де макрос - єдине практичне рішення.

2: Не маю на увазі, що інші відповіді не могли легко підійти до своїх рішень: вони просто вимагають навичок / знань, які я особисто не так легко під рукою. Але всі користувачі Vim знають, як використовувати регулярні команди редагування!


1

Просто додаючи "#" наприкінці плюс лічильник "c"

:let c=0 | g/^#\d\+.*/ let c+=1 | s//\='#' . c

Глобальна частина g/^#\d\+.*/ let c+=1дозволяє нам збільшувати лічильник лише в рядках, що мають шаблон


Хіба не те, що покрито гіпютрами?
D. Ben Knoble

У цьому випадку я думаю, що я придумав коротший спосіб зробити щось подібне, можливо, коментар буде кращим.
SergioAraujo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.