简体   繁体   中英

List comprehension to find matching parens

I'm trying to come up with a list comprehension that will match open and close parens. So far I have these two statements which grab two lists of open and close parens separately

my_str = "hanz(and(franz/bob())+ 7) + tom(2)"

Grab idx of open parens:

[ i for i,c in enumerate(my_str) if c == '(']

# [4, 8, 18, 31]

Grab idx of close parens:

[ i for i,c in enumerate(my_str) if c == ')']

# [19, 20, 24, 33]

What I would like is one handy comprehension that could give me a list of pairs corresponding to each match pair of parens

ie

[ ???? for i,c in enumerate(my_str) ???]

# [(4,24), (8,20), (18,19), (31,33)]

Like @Tordek mentioned, though not impossible this is not very practical,

For the sake of completeness however, here's a solution:

my_str = "hanz(and(franz/bob())+ 7) + tom(2)"

 pt_arr = [ 1 if c == '(' else -1 for i,c in enumerate(my_str ) if c == ')' or c == '(']
idx_arr = [ i for i,c in enumerate(my_str ) if c == ')' or c == '(']

[(idx_arr[strt_idx],idx_arr[strt_idx + [j for j,d in enumerate([ sum(pt_arr[strt_idx:i + 1]) for i,c in enumerate(pt_arr) if i >= strt_idx]) if d == 0][0]]) for strt_idx,f in enumerate(pt_arr) if f == 1]

# [(4,24), (8,20), (18,19), (31,33)]

As mentioned in comments, the proper and simple way to do this is using a stack:

my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
stack = []
parens = []
for i, c in enumerate(my_str):
    if c == "(":
        stack.append(i)
    elif c == ")":
        parens.append((stack.pop(), i))
print(parens) # [(18, 19), (8, 20), (4, 24), (31, 33)]

But if you value one-liners more than readability or coding conventions, you could also cram that into a list comprehensions with side effects:

stack = []
parens = [(stack.pop(), i) for i, c in enumerate(my_str)
          if c == "(" and stack.append(i) or c == ")"]
print(parens) # [(18, 19), (8, 20), (4, 24), (31, 33)]

This uses the fact that and and or are short-circuit-evaluated, thus it will append items only if c == "(" , but then fail because append return None , and only add elements to the results if the second condition, c == ")" is true, popping the position of the most recent ( from the stack.

At least, it is not a total abuse of list comprehensions, as the result is not discarded but actually the desired result, and it's probably still easier to comprehend than the three list comprehensions that you have (although those work without side effects), but the better solution for a "handy" way to do this would be: Make it a function, than it does not matter how many lines it has.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM