1

Imagine I have a file as follows,

10 10
10 11
...
10 20
20 20
20 21
...
20 40
40 40
40 41
...

I know simple regex such as

To search two numbers

/\d\+ \d\+

To search repeated numbers

/\(\d\+\) \1

My question is what is regex pattern such that I can search

10 20
20 40
40 80
...

I want to create regex pattern something of this sort

\(\d\+\) 2*\1

So that I can get multiplication of that earlier group

0

2 Answers 2

2

So you're looking for a regular expression feature wherein a back-reference refers not to a preceding capture group but to the result of applying a mathematical function to a preceding capture group. I'm not aware of any regex engine capable of doing this. That doesn't mean one doesn't exist; there are some pretty exotic/complex operations possible in some engines. But this definitely can't be done with Vim's RE engine.

So that's the answer. But that's not satisfying at all. Can it be done in Vim some other way? It certainly can with a custom function. "Anything" can be done that way. That's where I'd start barring anything cleaner or more elegant coming to mind first. (In real life I'd also consider farming the work out to the shell/command-line tools but this is Vim territory.) Anyways, ...

func! FindDoubled()
    while search('\v^\d+ \d+$', "W")
        let l = matchlist(getline("."), '\v^(\d+) (\d+)$')
        if l[2] == l[1] * 2
            return
        endif
    endwhile
    echom "Not match"
endfunc

This will match 10 20 and 20 40 in your example file. By match I mean the cursor will be positioned on the match and the function will return. You can repeatedly run it until the end of the file is reached.

It's all hardcoded, though, so not very interesting. To make it more useful you might parameterize the regex and/or the math operation. Something along these lines...

func! RelatedPairSearch(patt, rel)
    while search(a:patt, "W")
        let l = matchlist(getline("."), a:patt)
        if len(l[1]) && len(l[2]) && l[2] == eval(printf(a:rel, l[1]))
            return
        endif
    endwhile
    echom "No match"
endfunc

This takes a pattern and a string that expresses a math operation to be applied to the first submatch from the pattern. The result is compared to whatever the second submatch is. It behaves like the first function, i.e. stopping on matches and displaying a message when no more matches are found.

A call that matches the same lines as the first function might look like:

call RelatedPairSearch('\v^(\d+) (\d+)$', '%d * 2')

Out of laziness I'm using a printf token to indicate where the first capture group (submatch) value should be placed inside the expression. (There's also no input validation, checking for capture groups in the pattern, say. The idea is not to post production ready code.)

(This is really just toy code at this point but the call actually works.)

4
  • I deleted my first answer since it was not answering the right question. It's funny that both answers read it wrong the same way. I don't think either of us recognized the * in the last line as multiplication operator rather than wildcard. As it says above you can't do it with Vim regex alone. For the heck of it I've shown a custom coded way. Surely not the elegant solution you were hoping for but I don't think it gets much cleaner than this.
    – B Layer
    Sep 22, 2020 at 7:35
  • 1
    This is amazing answer. This what I was looking for. Tons of thanks. Sep 23, 2020 at 16:21
  • LOL. That certainly wasn't the reaction I was expecting. But I dig it! You're welcome. :)
    – B Layer
    Sep 23, 2020 at 19:02
  • Side note: the "W" in the search() call prevents the search from wrapping back to the start from the end of the buffer. That's a fairly arbitrary choice and if you'd prefer for it to wrap just replace that with lower case w: "w".
    – B Layer
    Sep 25, 2020 at 9:15
2

You can use submatch() function and use it to substitute with some pattern on those lines may be. I got it till here:

:%s/\(\d\+\).*\ \(\d\+\).*$/\=(submatch(2)==2*submatch(1))?"YES":"NO"/g

This replaces the lines where if finds the pattern that you requested for with YES.

Not sure how to do it without substitution.

Following for a better answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.