Good afternoon friends! The task is to write code that checks whether brackets are correctly placed in the expression, i.e.


("(5+5)/[4+4]*{2*2}" - True, "(3+[2*3)]" - False .


I wrote the code, to be honest, of course it's a shame to show it, but alas, no one except you will make me smarter. Please - tell me how you can make of this, the normal code? The code itself works.


 def checkio(expression): list_backets = [] for i in expression: if i == "[" or i == "]" or i == "(" or i == ")" or i == "{" or i == "}": list_backets.append(i) //создаю список нужных нам элементов if len(list_backets) == 0: //если нет элементов вовсе return True elif list_backets.count("[") == list_backets.count("]") and list_backets.count("(") == list_backets.count(")") and list_backets.count("{") == list_backets.count("}"): //если количество "(" равно количеству ")" и так далее... j = 0 try: while j != range(len(list_backets)): //попытка найти в списке соседние скобки if list_backets[j] == "(" and list_backets[j+1] == ")": list_backets.pop(j) list_backets.pop(j) j = 0 elif list_backets[j] == "{" and list_backets[j+1] == "}": list_backets.pop(j) list_backets.pop(j) j = 0 elif list_backets[j] == "[" and list_backets[j+1] == "]": list_backets.pop(j) list_backets.pop(j) j = 0 else: j += 1 except: if (len(list_backets)) == 0: return True else: return False else: return False 

It looks dumb and clumsy, if you have the opportunity and time, tell us how to make it more "beautiful" with explanations. Thank!

5 answers 5

Use spell checker

 list_backets -> brackets 

At the same time, the type name ( list ) is not required here. brackets almost every line is used and is in itself sufficiently readable.

Return boolean expressions directly

Instead:

 if (len(list_backets)) == 0: return True else: return False 

You can write simply:

 return (not list_brackets) 

empty collections in Python are considered False in a boolean context

Avoid bare except: which catches all types of exceptions.

This construction intercepts even KeyboardInterrupt ( Ctrl + C ) and SystemExit (exit from the script, the implementation of sys.exit() ) is undesirable. Catch only those types of exceptions that you expect, otherwise you can ignore input errors or errors caused by a bug in the code — unexpected errors should stop the script in this case.

In Python, it is sometimes permissible to use exceptions to control the flow of code, but in fact, use should be limited to cases where it greatly simplifies the code and improves its readability and efficiency.

In the code in question, the use of interception exceptions indicates a problem with looping. It is necessary to reorganize the code in order to exclude try / except in this case.

Comparing an integer with range() is a bug

Replace j != range(len(list_backets)) with j != len(brackets)

Avoid controlling the loop variable manually. Bypass the collection directly.

That is, instead of:

 j = 0 while j != len(brackets): print(brackets[j]) j += 1 

Write:

 for bracket in brackets: print(bracket) 

Your algorithm requires controlling the loop variable, which makes it less clear and (in this case) less efficient (looks like a quadratic algorithm). For comparison, here is a linear algorithm that bypasses the collection directly .

Use # instead of // for comments in Python

Avoid unnecessary code duplication.

Instead:

 i == "[" or i == "]" or i == "(" or i == ")" or i == "{" or i == "}" 

You can write:

 character in "[](){}" 

In this case, all the brackets are represented by a single character. To support brackets of several characters, you can put in a tuple:

 token in ('>>', '<<', ']', '[', '}', '{') 

Instead:

 (list_backets.count("[") == list_backets.count("]") and list_backets.count("(") == list_backets.count(")") and list_backets.count("{") == list_backets.count("}")) 

can write:

 all(brackets.count(opening) == brackets.count(closing) for opening, closing in zip("[({", "])}")) 

A small code repetition may be useful. To the extremes should not fall. Look at the situation, which code is more understandable and easier to change, without breaking, after six months, when the details are forgotten.

      list_backets.pop(j) list_backets.pop(j) j = 0 

    These pieces of code are clearly repeated, so it makes no sense to do 3 different if'a. Conditions can be written through or .

     except: if (len(list_backets)) == 0: return True else: return False 

    There was already a check for an empty string. Why is she here for the second time? And why is it generally expected that an exception will occur here? One hundred and then it must be redone, probably.

      list_backets.pop(j) list_backets.pop(j) 

    If I understand correctly, is this deletion from the middle of the list? Yes, and twice (once for each bracket). This is usually extremely inefficient, although I can't say about the python.

      list_backets.append(i) //создаю список нужных нам элементов 

    Why brackets in a separate list? You can pass on the line and immediately process.


    I propose this solution:

    Create a set of brackets (6 pieces) and a dictionary of matches of the closing brackets with an opening (closing - key, opening - value).

     завести пустой стек для каждого символа строки если он присутствует в наборе скобок если он отсутствует в словаре соответствий, т. е. это открывающая скобка добавить в стек этот символ иначе он закрывающая скобка если значение из словаря соответствий отличается от вершины стека return false иначе выкинуть последний элемент стека return стек пуст 
    • “Why is she here a second time?” Because the list length changes as the code executes. - jfs

    I think the best solution would be to use the stack and permanently remove elements in it, if the brackets opening and closing match, they are cleared from the stack and so on until it is empty.

     def check(string): brackets_open = ('(', '[', '{', '<') brackets_closed = (')', ']', '}', '>') stack = [] for i in string: if i in brackets_open: stack.append(i) if i in brackets_closed: if len(stack) == 0: return False index = brackets_closed.index(i) open_bracket = brackets_open[index] if stack[-1] == open_bracket: stack = stack[:-1] else: return False return (not stack) 

    The application is very simple: create strings that need to be checked for correctness.

     str1 = '[{([[[<>]]])(<>)(){}}]' str2 = ']()(){<>}[[()]]' str3 = '[(sjd),"2"],{2:3}, [<>]' str4 = '{[[[[((()))]]<]>]}' 

    Then we apply the function to them:

     print(check(str1)) #True print(check(str2)) #False print(check(str3)) #True print(check(str4)) #False 

    I came up with such a visual solution, without syntax trees or anything else. Imagine that each opening bracket is an elementary particle. The closing bracket is the opposite. If the string is valid, then the particles must be annihilated. If something is left, the string is invalid. During the test, the brackets are thrown into the general pile, and if they suddenly find the opposite pair - everything explodes, ololo. In essence, this is a rephrased and less productive solution from the top answer.

     class ElectronPositronValidator: def __init__(self): self.op_brackets = ("(", "[", "{", "<") self.clos_brackets = (")", "]", "}", ">") self.opposite_brackets = {"(": ")", "[": "]", "{": "}", "<": ">"} self.bracket_dump = "" def _add_to_dump(self, bracket): self.bracket_dump = self.bracket_dump + bracket if bracket in self.clos_brackets: # Проверим на предмет аннигиляции две последние скобки if len(self.bracket_dump) >= 2 and \ self.bracket_dump[-1] == self.opposite_brackets.get(self.bracket_dump[-2], None): self._annihilate() def _annihilate(self): self.bracket_dump = self.bracket_dump[:-2] def is_valid(self, target_string): self.bracket_dump = '' for char in target_string: if char in self.op_brackets or char in self.clos_brackets: self._add_to_dump(char) if self.bracket_dump: return False else: return True validator = ElectronPositronValidator() print(validator.is_valid("(5+5)/[4+4]*{2*2}")) print(validator.is_valid("()()()()()()()()()()")) print(validator.is_valid("([{<|>}])")) print(validator.is_valid("3[||:||]Ɛ")) print(validator.is_valid("")) print(validator.is_valid("(3+[2*3)]")) print(validator.is_valid("()()()()()()()()()(")) print(validator.is_valid("()[]{}<>}{")) print(validator.is_valid("({{[[ > ]]}})")) print(validator.is_valid(">([])<")) 

    Good for all! Accidentally came across this post and came up with this: 1. At least there should be an even number of brackets - they opened / closed. 2. Use regex!

     import re exp = '{*08/)348+124[7{)7{/[){5[}506(}*178][]/-{71)]7[19{05{-{*)}7(5+2)54]43-*2+(*74]{44}){52{/{0]88{(})9}90}}50[)(*41{{8+)-/38/5/*({0(-28*21/8{+{*9*22-/269+44-[/)298043{40]75/-1[4[26(/4]3-{71-]{554}65*6{20558*6{*/32)-{6++*/92]+623{1-59+/749291)161)(((-21-+++[0138)3*3+]+3{[4)952})124/3]9/)]/2+})(-964}' exp_mod = ''.join([el for el in exp if el in ('(',')','{','}','[',']')]) #убираем все кроме скобок if len(exp_mod) % 2 != 0: print(False) #если нечетно количество - сразу в утиль elif len(re.findall(r'\((?=[\]\}])|\[(?=[\)\}])|\{(?=[\)\]])', exp_mod)) > 0: print(False) #если после открытия сразу есть иное закрытие (например, после '{' идет ']')- тоже в утиль else: # ищем соотв пары '()','[]','{}' и те же пары, только с 2, 4 и далее другими скобками м/у ними - ну тут напутано, можно и попроще pattern = r'\(\)|\[\]|\{\}|\(.{2}\)|\[.{2}\]|\{.{2}\}|\(.{4}\)|\[.{4}\]|\{.{4}\}|\(.{' + str(len(exp_mod)-4) + '}\)|\[.{' + str(len(exp_mod)-4) + '}\]|\{.{' + str(len(exp_mod)-4) + '}\}|\(.{' + str(len(exp_mod)-2) + '}\)|\[.{' + str(len(exp_mod)-2) + '}\]|\{.{' + str(len(exp_mod)-2) + '}\}|\(.{' + str(len(exp_mod)-8) + '}\)|\[.{' + str(len(exp_mod)-8) + '}\]|\{.{' + str(len(exp_mod)-8) + '}\}' if len(re.sub(pattern, '', exp_mod)) > 0: print(False) else: print(True)