рдЕрдирд┐рд╢реНрдЪрд┐рддрдХрд╛рд▓реАрди рдЧрд╣рд░рд╛рдИ рдХреА рдиреЗрд╕реНрдЯреЗрдб рд╕реВрдЪрд┐рдпреЛрдВ рдХреЛ рдЦреЛрд▓рдирд╛

рдЖрдЬ рдореИрдВ рдЕрдирд┐рд╢реНрдЪрд┐рддрдХрд╛рд▓реАрди рдЧрд╣рд░рд╛рдИ рдХреА рдиреЗрд╕реНрдЯреЗрдб рд╕реВрдЪреА рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░рдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛ред рдпрд╣ рдПрдХ рдХрд╛рдлреА рдЧреИрд░-рддреБрдЪреНрдЫ рдХрд╛рд░реНрдп рд╣реИ, рдЗрд╕рд▓рд┐рдП рдореИрдВ рдпрд╣рд╛рдВ рдмрд╛рдд рдХрд░рдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛ рдХрд┐ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреНрдпрд╛ рд╣реИрдВ, рдЙрдирдХреЗ рдкреЗрд╢реЗрд╡рд░реЛрдВ рдФрд░ рд╡рд┐рдкрдХреНрд╖ рдФрд░ рдЙрдирдХреЗ рдкреНрд░рджрд░реНрд╢рди рдХреА рддреБрд▓рдирд╛ред


рд▓реЗрдЦ рдореЗрдВ рдиреАрдЪреЗ рдХрдИ рдЦрдВрдб рд╢рд╛рдорд┐рд▓ рд╣реЛрдВрдЧреЗ:


  • рдХрд╛рд░реНрдпреЛрдВ
  • рдбреЗрдЯрд╛
  • рдкрд░рд┐рдгрд╛рдо
  • рдирд┐рд╖реНрдХрд░реНрд╖

рднрд╛рдЧ 1. рдХрд╛рд░реНрдп


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдЙрдзрд╛рд░


outer_flatten_1


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
def outer_flatten_1(array: Iterable) -> List: """ Based on C realization of this solution More on: https://iteration-utilities.readthedocs.io/en/latest/generated/deepflatten.html https://github.com/MSeifert04/iteration_utilities/blob/384948b4e82e41de47fa79fb73efc56c08549b01/src/deepflatten.c """ return deepflatten(array) 

рдореИрдВрдиреЗ рдПрдХ рдмрд╛рд╣рд░реА рдкреИрдХреЗрдЬ, iteration_utilities рд╕реЗ рдкрд╛рд░реНрд╕рд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рд▓рд┐рдпрд╛ред


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╕реА рдореЗрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рдЕрдЬрдЧрд░ рдХреЛ рдПрдХ рдЙрдЪреНрдЪ-рд╕реНрддрд░реАрдп рдлрд╝рдВрдХреНрд╢рди рдХреЙрд▓ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ рд▓рд┐рдП рдЫреЛрдбрд╝рдХрд░ред
C рдореЗрдВ рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдмрд▓реНрдХрд┐ рдмреЛрдЭрд┐рд▓ рд╣реИ, рдЖрдк рдЗрд╕реЗ рд╕реНрдкреЙрдЗрд▓рд░ рдореЗрдВ рд▓рд┐рдВрдХ рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░рдХреЗ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВред рдлрд╝рдВрдХреНрд╢рди рдПрдХ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рд╣реИред


 >>> from typing import Iterator, Generator >>> from iteration_utilities import deepflatten >>> isinstance(deepflatten(a), Iterator) True >>> isinstance(deepflatten(a), Generator) False 

рдпрд╣рд╛рдВ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреА рдЬрдЯрд┐рд▓рддрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХрд╣рдирд╛ рдореБрд╢реНрдХрд┐рд▓ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдореИрдВ рдЗрд╕ рд░реБрдЪрд┐ рдХреЛ рд╣реИрдмрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдкрд░ рдЫреЛрдбрд╝ рджреЗрддрд╛ рд╣реВрдВред


рд╕рд╛рдорд╛рдиреНрдп рддреМрд░ рдкрд░, рдореИрдВ рдпрд╣ рднреА рдзреНрдпрд╛рди рджреЗрдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛ рдХрд┐ рдЗрд╕ рдкреБрд╕реНрддрдХрд╛рд▓рдп рд╕реЗ рдЕрдиреНрдп рд╕рднреА рдХрд╛рд░реНрдп рдХрд╛рдлреА рддреЗрдЬ рд╣реИрдВ рдФрд░ рд╕реА рдореЗрдВ рднреА рд▓рд╛рдЧреВ рдХрд┐рдП рдЧрдП рд╣реИрдВред


outer_flatten_2


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 def outer_flatten_2(array: Iterable) -> List: """ recursive algorithm, vaguely reminiscent of recursion_flatten. Based on next pattern: .. code:: python try: tree = iter(node) except TypeError: yield node more on: https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collapse """ return collapse(array) 

рдиреЗрд╕реНрдЯреЗрдб рд╕реВрдЪрд┐рдпреЛрдВ рдХреЛ рдЕрдирдкреИрдХ рдХрд░рдиреЗ рдХреА рдЗрд╕ рдкрджреНрдзрддрд┐ рдХрд╛ рдХреНрд░рд┐рдпрд╛рдиреНрд╡рдпрди рдмрд╛рд╣рд░реА рдкреИрдХреЗрдЬ рдореЗрдВ рднреА рд╣реЛрддрд╛ рд╣реИ, рдЕрд░реНрдерд╛рддреН рдЕрдзрд┐рдХ_рдкрд░рд┐рдЯреВрд▓ред
рдпрд╣ рдХрд╛рд░реНрдп рдореЗрд░реЗ рд╡рд┐рдЪрд╛рд░ рд╕реЗ рд╢реБрджреНрдз рдЕрдЬрдЧрд░ рдкрд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╖реНрдЯрддрдо рдирд╣реАрдВред рдкреНрд░рд▓реЗрдЦрди рд▓рд┐рдВрдХ рдореЗрдВ рдПрдХ рд╡рд┐рд╕реНрддреГрдд рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рджреЗрдЦрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред
рдЗрд╕ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреА рдЬрдЯрд┐рд▓рддрд╛ рд╣реЗ (n * m) рд╣реИред


рдЦреБрдж рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди


niccolum_flatten


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 def niccolum_flatten(array: Iterable) -> List: """ Non recursive algorithm Based on pop/insert elements in current list """ new_array = array[:] ind = 0 while True: try: while isinstance(new_array[ind], list): item = new_array.pop(ind) for inner_item in reversed(item): new_array.insert(ind, inner_item) ind += 1 except IndexError: break return new_array 

рдЬрдм рдореИрдВрдиреЗ рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рд░реВрдк рд╕реЗ @ru_python_beginners рдХреЗ рдЯреЗрд▓реАрдЧреНрд░рд╛рдо рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХреА, рдЕрдирд┐рд╢реНрдЪрд┐рддрдХрд╛рд▓реАрди рдШреЛрдВрд╕рд▓реЗ рдХреЗ рд╢рд┐рдХрд╛рд░ рдХреА рдиреЗрд╕реНрдЯреЗрдб рд╕реВрдЪрд┐рдпреЛрдВ рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ, рдореИрдВрдиреЗ рдЕрдкрдирд╛ рд╕рдВрд╕реНрдХрд░рдг рдкреНрд░рд╕реНрддрд╛рд╡рд┐рдд рдХрд┐рдпрд╛ред


рдпрд╣ рдЗрд╕ рддрдереНрдп рдореЗрдВ рд╢рд╛рдорд┐рд▓ рд╣реИ рдХрд┐ рд╣рдо рдореВрд▓ рд╕реВрдЪреА рдХреА рдирдХрд▓ рдХрд░рддреЗ рд╣реИрдВ (рдЗрд╕рд▓рд┐рдП рдЗрд╕реЗ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдирд╣реАрдВ), рдФрд░ рдлрд┐рд░ рдЯреНрд░реВ рд▓реВрдк рдореЗрдВ рд╣рдо рдЬрд╛рдВрдЪрддреЗ рд╣реИрдВ рдХрд┐ рдЖрдЗрдЯрдо рдПрдХ рд╕реВрдЪреА рд╣реИ - рд╣рдо рдЗрд╕рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЬрд╛рддреЗ рд╣реИрдВ рдФрд░ рдкрд░рд┐рдгрд╛рдо рдХреЛ рдмрд╣реБрдд рд╢реБрд░реБрдЖрдд рдореЗрдВ рд╕рдореНрдорд┐рд▓рд┐рдд рдХрд░рддреЗ рд╣реИрдВред


рд╣рд╛рдВ, рдЕрдм рдореИрдВ рд╕рдордЭрддрд╛ рд╣реВрдВ рдХрд┐ рдпрд╣ рдЗрд╖реНрдЯрддрдо рдФрд░ рдХрдард┐рди рдирд╣реАрдВ рджрд┐рдЦрддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ reindexing рд╣рд░ рдмрд╛рд░ рд╣реЛрддрд╛ рд╣реИ (рдЪреВрдВрдХрд┐ рд╣рдо рд╕реВрдЪреА рдореЗрдВ рдКрдкрд░ рд╕реЗ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ рдФрд░ рд╣рдЯрд╛рддреЗ рд╣реИрдВ), рд╣рд╛рд▓рд╛рдВрдХрд┐ рдЗрд╕ рд╡рд┐рдХрд▓реНрдк рдореЗрдВ рдЬреАрд╡рди рдХрд╛ рдЕрдзрд┐рдХрд╛рд░ рднреА рд╣реИ рдФрд░ рд╣рдо рдмрд╛рдХреА рдХреЗ рд╕рд╛рде рдЗрд╕рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреА рдЬрд╛рдВрдЪ рдХрд░реЗрдВрдЧреЗред


рдкрд╛рд░рд┐рдд рдХрд┐рдП рдЧрдП рдкреНрд░рддреНрдпреЗрдХ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдХреЗ рд▓рд┐рдП рджреЛ рдмрд╛рд░ рд╕реВрдЪреА рдХреЗ рдкреБрдирд░реНрдирд┐рд░реНрдорд╛рдг рдХреЗ рдХрд╛рд░рдг рдЗрд╕ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреА рдЬрдЯрд┐рд▓рддрд╛ рд╣реЗ (n ^ 3 * m) рд╣реИ


tishka_flatten


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 def tishka_flatten(data: Iterable) -> List: """ Non recursive algorithm Based on append/extend elements to new list """ nested = True while nested: new = [] nested = False for i in data: if isinstance(i, list): new.extend(i) nested = True else: new.append(i) data = new return data 

рдкрд╣рд▓реЗ рдореЗрдВ рд╕реЗ рдПрдХ рдиреЗ рдЯрд┐рд╕реНрдХрд╛ 17 рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рднреА рджрд┐рдЦрд╛рдпрд╛ред рдпрд╣ рдЗрд╕ рддрдереНрдп рдореЗрдВ рд╕рдорд╛рд╣рд┐рдд рд╣реИ рдХрд┐ рдиреЗрд╕реНрдЯреЗрдб рд▓реВрдк рдХреЗ рдЕрдВрджрд░ рдореМрдЬреВрджрд╛ рдиреЗрд╕реНрдЯреЗрдб рдиреЗрд╕реНрдЯреЗрдб = рдлрд╛рд▓реНрд╕ рдХреЗ рд╕рд╛рде рдореМрдЬреВрдж рд▓рд┐рд╕реНрдЯ рд╕реЗ рдЕрдзрд┐рдХ рдкреБрдирд░рд╛рд╡реГрддреНрдд рд╣реЛрддрд╛ рд╣реИ, рдФрд░ рдЕрдЧрд░ рдЗрд╕рдореЗрдВ рдХрдо рд╕реЗ рдХрдо рдПрдХ рдмрд╛рд░ рд╢реАрдЯ рдорд┐рд▓ рдЬрд╛рддреА рд╣реИ, рддреЛ рдпрд╣ рдиреЗрд╕реНрдЯреЗрдб = рдЯреНрд░реВ рдлреНрд▓реИрдЧ рдЫреЛрдбрд╝ рджреЗрддрд╛ рд╣реИ рдФрд░ рдЗрд╕ рд╢реАрдЯ рдХреЛ рд▓рд┐рд╕реНрдЯ рдореЗрдВ рдмрдврд╝рд╛ рджреЗрддрд╛ рд╣реИред рддрджрдиреБрд╕рд╛рд░, рдпрд╣ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ рдкреНрд░рддреНрдпреЗрдХ рд░рди рдХреЗ рд▓рд┐рдП рдпрд╣ рдПрдХ рд╕реНрддрд░ рдХреЗ рдШреЛрдВрд╕рд▓реЗ рдХреЗ рд╢рд┐рдХрд╛рд░ рдХреЛ рджреЗрддрд╛ рд╣реИ, рдШреЛрдВрд╕рд▓реЗ рдХреЗ рдХрд┐рддрдиреЗ рд╕реНрддрд░ рд╣реЛрдВрдЧреЗ - рдЪрдХреНрд░ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЗрддрдиреЗ рд╕рд╛рд░реЗ рдорд╛рд░реНрдЧ рд╣реЛрдВрдЧреЗред рдЬреИрд╕рд╛ рдХрд┐ рд╡рд┐рд╡рд░рдг рд╕реЗ рджреЗрдЦрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ - рд╕рдмрд╕реЗ рдЗрд╖реНрдЯрддрдо рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдирд╣реАрдВ, рд▓реЗрдХрд┐рди рдлрд┐рд░ рднреА, рдпрд╣ рдмрд╛рдХреА рд╕реЗ рдЕрд▓рдЧ рд╣реИред
рдЗрд╕ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреА рдЬрдЯрд┐рд▓рддрд╛ рд╣реЗ (n * m) рд╣реИред


zart_flatten


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 def zart_flatten(a: Iterable) -> List: """ Non recursive algorithm Based on pop from old and append elements to new list """ queue, out = [a], [] while queue: elem = queue.pop(-1) if isinstance(elem, list): queue.extend(elem) else: out.append(elem) return out[::-1] 

рдПрдХ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рднреА рдЕрдиреБрднрд╡реА рдЪреИрдЯ рдкреНрд░рддрд┐рднрд╛рдЧрд┐рдпреЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ рджреНрд╡рд╛рд░рд╛ рд╕реБрдЭрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИред рдореЗрд░реА рд░рд╛рдп рдореЗрдВ, рдпрд╣ рдХрд╛рдлреА рд╕рд░рд▓ рдФрд░ рд╕рд╛рдл рд╣реИред рдЗрд╕рдХрд╛ рдЕрд░реНрде рдпрд╣ рд╣реИ рдХрд┐ рдпрджрд┐ рддрддреНрд╡ рдПрдХ рд╕реВрдЪреА рд╣реИ, рддреЛ рд╣рдо рдкрд░рд┐рдгрд╛рдо рдХреЛ рдореВрд▓ рд╕реВрдЪреА рдХреЗ рдЕрдВрдд рдореЗрдВ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ, рдФрд░ рдпрджрд┐ рдирд╣реАрдВ, рддреЛ рд╣рдо рдЗрд╕реЗ рдЖрдЙрдЯрдкреБрдЯ рдореЗрдВ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВред рдореИрдВ рдЗрд╕реЗ рд╡рд┐рд╕реНрддрд╛рд░ / рдкрд░рд┐рд╢рд┐рд╖реНрдЯ рддрдВрддреНрд░ рдХреЗ рдиреАрдЪреЗ рдХрд╣реВрдВрдЧрд╛ред рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк, рд╣рдо рддрддреНрд╡реЛрдВ рдХреЗ рдореВрд▓ рдХреНрд░рдо рдХреЛ рд╕рдВрд░рдХреНрд╖рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрд▓реНрдЯреЗ рдкрд░рд┐рдгрд╛рдореА рдлреНрд▓реИрдЯ рд╕реВрдЪреА рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рддреЗ рд╣реИрдВред


рдЗрд╕ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреА рдЬрдЯрд┐рд▓рддрд╛ рд╣реЗ (n * m) рд╣реИред


recursive_flatten_iterator


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 def recursive_flatten_iterator(arr: Iterable) -> Iterator: """ Recursive algorithm based on iterator Usual solution to this problem """ for i in arr: if isinstance(i, list): yield from recursion_flatten(i) else: yield i 

рд╕рдВрднрд╡рддрдГ рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХрд╛ рд╕рдмрд╕реЗ рдЖрдо рд╕рдорд╛рдзрд╛рди рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдФрд░ рдЙрдкрдЬ рд╕реЗ рд╣реИред рдереЛрдбрд╝рд╛ рдХрд╣рд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ - рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХрд╛рдлреА рд╕рд░рд▓ рдФрд░ рдХреБрд╢рд▓ рд▓рдЧрддрд╛ рд╣реИ, рдЗрд╕ рддрдереНрдп рдХреЗ рдЕрд▓рд╛рд╡рд╛ рдХрд┐ рдпрд╣ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░, рдмрдбрд╝реЗ рдмрд╛рдбрд╝реЛрдВ рдХреЗ рд╕рд╛рде, рдХреЙрд▓ рдХрд╛ рдПрдХ рдмрдбрд╝рд╛ рдвреЗрд░ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред


рдЗрд╕ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреА рдЬрдЯрд┐рд▓рддрд╛ рд╣реЗ (n * m) рд╣реИред


recursive_flatten_generator


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 def recursive_flatten_generator(array: Iterable) -> List: """ Recursive algorithm Looks like recursive_flatten_iterator, but with extend/append """ lst = [] for i in array: if isinstance(i, list): lst.extend(recursive_flatten_list(i)) else: lst.append(i) return lst 

рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рдкреВрд░реНрд╡рд╡рд░реНрддреА рдХреЗ рд╕рдорд╛рди рд╣реИ, рдпрд╣ рдХреЗрд╡рд▓ рдПрдХ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдирд╣реАрдВ, рдмрд▓реНрдХрд┐ рдкреБрдирд░рд╛рд╡рд░реНрддреА рд╡рд┐рд╕реНрддрд╛рд░ / рдкрд░рд┐рд╢рд┐рд╖реНрдЯ рддрдВрддреНрд░ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред


рдЗрд╕ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреА рдЬрдЯрд┐рд▓рддрд╛ рд╣реЗ (n * m) рд╣реИред


tishka_flatten_with_stack


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 def tishka_flatten_with_stack(seq: Iterable) -> List: """ Non recursive algorithm Based on zart_flatten, but build on try/except pattern """ stack = [iter(seq)] new = [] while stack: i = stack.pop() try: while True: data = next(i) if isinstance(data, list): stack.append(i) i = iter(data) else: new.append(data) except StopIteration: pass return new 

рдПрдХ рдФрд░ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдЬреЛ рдЯрд┐рд╕реНрдХрд╛ рдиреЗ рдкреНрд░рджрд╛рди рдХрд┐рдпрд╛, рдЬреЛ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, zart_flatten рдХреЗ рд╕рдорд╛рди рд╣реИ, рд╣рд╛рд▓рд╛рдВрдХрд┐, рдЗрд╕реЗ рдПрдХ рд╕рд╛рдзрд╛рд░рдг рд╡рд┐рд╕реНрддрд╛рд░ / рдкрд░рд┐рд╢рд┐рд╖реНрдЯ рддрдВрддреНрд░ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рдлрд┐рд░ рдПрдХ рдЕрдирдВрдд рд▓реВрдк рдореЗрдВ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЬрд┐рд╕рдореЗрдВ рдЗрд╕ рддрдВрддреНрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдпрд╣ zart_flatten рдХреЗ рд╕рдорд╛рди рдХреБрдЫ рдирд┐рдХрд▓рд╛, рд▓реЗрдХрд┐рди рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдФрд░ рдЬрдмрдХрд┐ Trueред


рдЗрд╕ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреА рдЬрдЯрд┐рд▓рддрд╛ рд╣реЗ (n * m) рд╣реИред


рднрд╛рдЧ 2. рдбреЗрдЯрд╛


рдЗрди рдПрд▓реНрдЧреЛрд░рд┐рджрдо рдХреА рдкреНрд░рднрд╛рд╡рд╢реАрд▓рддрд╛ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рдШреЛрдВрд╕рд▓реЗ рдХреЗ рд╢рд┐рдХрд╛рд░ рдХреА рд╕реВрдЪрд┐рдпреЛрдВ рдХреА рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдкреАрдврд╝реА рдХреЗ рд▓рд┐рдП рдлрд╝рдВрдХреНрд╢рдВрд╕ рдмрдирд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЬрд┐рдирд╕реЗ рдореИрдВрдиреЗ рд╕рдлрд▓рддрд╛рдкреВрд░реНрд╡рдХ рдирд┐рдкрдЯрд╛ рд╣реИ рдФрд░ рдЖрдкрдХреЛ рдиреАрдЪреЗ рдкрд░рд┐рдгрд╛рдо рджрд┐рдЦрд╛рдПрдЧрд╛:


create_data_decreasing_depth


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 def create_data_decreasing_depth( data: Union[List, Iterator], length: int, max_depth: int, _current_depth: int = None, _result: List = None ) -> List: """ creates data in depth on decreasing. Examples: >>> data = create_data_decreasing_depth(list(range(1, 11)), length=5, max_depth=3) >>> assert data == [[[1, 2, 3, 4, 5], 6, 7, 8, 9, 10]] >>> data = create_data_decreasing_depth(list(range(1, 11)), length=2, max_depth=3) >>> assert data == [[[1, 2], 3, 4], 5, 6], [[7, 8,] 9, 10]] """ _result = _result or [] _current_depth = _current_depth or max_depth data = iter(data) if _current_depth - 1: _result.append(create_data_decreasing_depth( data=data, length=length, max_depth=max_depth, _current_depth=_current_depth - 1, _result=_result)) try: _current_length = length while _current_length: item = next(data) _result.append(item) _current_length -= 1 if max_depth == _current_depth: _result += create_data_decreasing_depth( data=data, length=length, max_depth=max_depth) return _result except StopIteration: return _result 

рдпрд╣ рдлрдВрдХреНрд╢рди рдШрдЯрддреА рдиреЗрд╕реНрдЯрд┐рдВрдЧ рдХреЗ рд╕рд╛рде рдиреЗрд╕реНрдЯреЗрдб рд▓рд┐рд╕реНрдЯ рдмрдирд╛рддрд╛ рд╣реИред


  >>> data = create_data_decreasing_depth(list(range(1, 11)), length=5, max_depth=3) >>> assert data == [[[1, 2, 3, 4, 5], 6, 7, 8, 9, 10]] >>> data = create_data_decreasing_depth(list(range(1, 11)), length=2, max_depth=3) >>> assert data == [[[1, 2], 3, 4], 5, 6], [[7, 8,] 9, 10]] 

create_data_increasing_depth


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 def create_data_increasing_depth( data: Union[List, Iterator], length: int, max_depth: int, _current_depth: int = None, _result: List = None ) -> List: """ creates data in depth to increase. Examples: >>> data = create_data_increasing_depth(list(range(1, 11)), length=5, max_depth=3) >>> assert data == [1, 2, 3, 4, 5, [6, 7, 8, 9, 10]] >>> data = create_data_increasing_depth(list(range(1, 11)), length=2, max_depth=3) >>> assert data == [1, 2, [3, 4, [5, 6]]], 7, 8, [9, 10]] """ _result = _result or [] _current_depth = _current_depth or max_depth data = iter(data) try: _current_length = length while _current_length: item = next(data) _result.append(item) _current_length -= 1 except StopIteration: return _result if _current_depth - 1: tmp_res = create_data_increasing_depth( data=data, length=length, max_depth=max_depth, _current_depth=_current_depth - 1) if tmp_res: _result.append(tmp_res) if max_depth == _current_depth: tmp_res = create_data_increasing_depth( data=data, length=length, max_depth=max_depth) if tmp_res: _result += tmp_res return _result 

рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рдмрдврд╝рддреА рдиреЗрд╕реНрдЯрд┐рдВрдЧ рдХреЗ рд╕рд╛рде рдиреЗрд╕реНрдЯреЗрдб рд╕реВрдЪреА рдмрдирд╛рддрд╛ рд╣реИред


  >>> data = create_data_increasing_depth(list(range(1, 11)), length=5, max_depth=3) >>> assert data == [1, 2, 3, 4, 5, [6, 7, 8, 9, 10]] >>> data = create_data_increasing_depth(list(range(1, 11)), length=2, max_depth=3) >>> assert data == [1, 2, [3, 4, [5, 6]]], 7, 8, [9, 10]] 

generate_data


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 def generate_data() -> List[Tuple[str, Dict[str, Union[range, Num]]]]: """ Generated collections of Data by pattern {amount_item}_amount_{length}_length_{max_depth}_max_depth where: .. py:attribute:: amount_item: len of flatten elements .. py:attribute:: length: len of elements at the same level of nesting .. py:attribute:: max_depth: highest possible level of nesting """ data = [] amount_of_elements = [10 ** i for i in range(5)] data_template = '{amount_item}_amount_{length}_length_{max_depth}_max_depth' # amount_item doesn't need to be [1] for amount_item in amount_of_elements[1:]: for max_depth in amount_of_elements: # for exclude flatten list after generate data by create_data_increasing_depth if amount_item > max_depth: # generate four types of length for length in range(0, max_depth + 1, math.ceil(max_depth / 4)): # min length must be 1 length = length or 1 data_name = data_template.format( amount_item=amount_item, length=length, max_depth=max_depth ) data_value = { 'data': range(amount_item), 'length': length, 'max_depth': max_depth } data.append((data_name, data_value)) # for not to produce more than 1 flat entity if max_depth == 1: break # this order is convenient for me data = sorted(data, key=lambda x: [x[1]['data'][-1], x[1]['max_depth'], x[1]['length']]) return data 

рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рд╕реАрдзреЗ рдкрд░реАрдХреНрд╖рдг рдбреЗрдЯрд╛ рдХреЗ рд▓рд┐рдП рдкреИрдЯрд░реНрди рдмрдирд╛рддрд╛ рд╣реИред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдпрд╣ рдЯреЗрдореНрдкрд▓реЗрдЯ рджреНрд╡рд╛рд░рд╛ рдмрдирд╛рдП рдЧрдП рд╣реЗрдбрд░ рдЙрддреНрдкрдиреНрди рдХрд░рддрд╛ рд╣реИред


 data_template = '{amount_item}_amount_{length}_length_{max_depth}_max_depth' 

рдЬрд╣рд╛рдВ:


  • рд░рд╛рд╢рд┐ - рд╕реВрдЪреА рдореЗрдВ рд╡рд╕реНрддреБрдУрдВ рдХреА рдХреБрд▓ рд╕рдВрдЦреНрдпрд╛
  • рд▓рдВрдмрд╛рдИ - рдПрдХ рдШреЛрдВрд╕рд▓реЗ рдХреЗ рд╕реНрддрд░ рдкрд░ рддрддреНрд╡реЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛
  • max_depth - рдЕрдзрд┐рдХрддрдо рдШреЛрдВрд╕рд▓реЗ рдХреЗ рд╕реНрддрд░ рдХреА рд╕рдВрдЦреНрдпрд╛

рдЖрд╡рд╢реНрдпрдХ рдбреЗрдЯрд╛ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдбреЗрдЯрд╛ рдХреЛ рдКрдкрд░ рджрд┐рдП рдЧрдП рдХрд╛рд░реНрдпреЛрдВ рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рддрджрдиреБрд╕рд╛рд░, рдЖрдЙрдЯрдкреБрдЯ, рдЬреИрд╕рд╛ рдХрд┐ рд╣рдо рдмрд╛рдж рдореЗрдВ рджреЗрдЦреЗрдВрдЧреЗ, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдбреЗрдЯрд╛ (рд╣реЗрдбрд░) рд╣реЛрдВрдЧреЗ:


 10_amount_1_length_1_max_depth 100_amount_1_length_1_max_depth 100_amount_1_length_10_max_depth 100_amount_3_length_10_max_depth 100_amount_6_length_10_max_depth 100_amount_9_length_10_max_depth 1000_amount_1_length_1_max_depth 1000_amount_1_length_10_max_depth 1000_amount_3_length_10_max_depth 1000_amount_6_length_10_max_depth 1000_amount_9_length_10_max_depth 1000_amount_1_length_100_max_depth 1000_amount_25_length_100_max_depth 1000_amount_50_length_100_max_depth 1000_amount_75_length_100_max_depth 1000_amount_100_length_100_max_depth 10000_amount_1_length_1_max_depth 10000_amount_1_length_10_max_depth 10000_amount_3_length_10_max_depth 10000_amount_6_length_10_max_depth 10000_amount_9_length_10_max_depth 10000_amount_1_length_100_max_depth 10000_amount_25_length_100_max_depth 10000_amount_50_length_100_max_depth 10000_amount_75_length_100_max_depth 10000_amount_100_length_100_max_depth 10000_amount_1_length_1000_max_depth 10000_amount_250_length_1000_max_depth 10000_amount_500_length_1000_max_depth 10000_amount_750_length_1000_max_depth 10000_amount_1000_length_1000_max_depth 

рднрд╛рдЧ 3. рдкрд░рд┐рдгрд╛рдо


рд╕реАрдкреАрдпреВ рдХреЛ рдкреНрд░реЛрдлрд╛рдЗрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП - рдореИрдВрдиреЗ рд▓рд╛рдЗрди_рдкреНрд░реЛрдлрд╛рдЗрд▓рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛
рд░реЗрдЦрд╛рдВрдХрди рдХреЗ рд▓рд┐рдП - timeit + matplotlib


рд╕реАрдкреАрдпреВ рдкреНрд░реЛрдлрд╛рдЗрд▓рд░


рдирд┐рд╖реНрдХрд░реНрд╖
 $ kernprof -l funcs.py $ python -m line_profiler funcs.py.lprof Timer unit: 1e-06 s Total time: 1.7e-05 s File: funcs.py Function: outer_flatten_1 at line 11 Line # Hits Time Per Hit % Time Line Contents ============================================================== 11 @profile 12 def outer_flatten_1(array: Iterable) -> List: 13 """ 14 Based on C realization of this solution 15 More on: 16 17 https://iteration-utilities.readthedocs.io/en/latest/generated/deepflatten.html 18 19 https://github.com/MSeifert04/iteration_utilities/blob/384948b4e82e41de47fa79fb73efc56c08549b01/src/deepflatten.c 20 """ 21 2 17.0 8.5 100.0 return deepflatten(array) Total time: 3.3e-05 s File: funcs.py Function: outer_flatten_2 at line 24 Line # Hits Time Per Hit % Time Line Contents ============================================================== 24 @profile 25 def outer_flatten_2(array: Iterable) -> List: 26 """ 27 recursive algorithm, vaguely reminiscent of recursion_flatten. Based on next pattern: 28 29 .. code:: python 30 31 try: 32 tree = iter(node) 33 except TypeError: 34 yield node 35 36 more on: 37 https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collapse 38 """ 39 2 33.0 16.5 100.0 return collapse(array) Total time: 0.105099 s File: funcs.py Function: niccolum_flatten at line 42 Line # Hits Time Per Hit % Time Line Contents ============================================================== 42 @profile 43 def niccolum_flatten(array: Iterable) -> List: 44 """ 45 Non recursive algorithm 46 Based on pop/insert elements in current list 47 """ 48 49 2 39.0 19.5 0.0 new_array = array[:] 50 2 6.0 3.0 0.0 ind = 0 51 2 2.0 1.0 0.0 while True: 52 20002 7778.0 0.4 7.4 try: 53 21010 13528.0 0.6 12.9 while isinstance(new_array[ind], list): 54 1008 1520.0 1.5 1.4 item = new_array.pop(ind) 55 21014 13423.0 0.6 12.8 for inner_item in reversed(item): 56 20006 59375.0 3.0 56.5 new_array.insert(ind, inner_item) 57 20000 9423.0 0.5 9.0 ind += 1 58 2 2.0 1.0 0.0 except IndexError: 59 2 2.0 1.0 0.0 break 60 2 1.0 0.5 0.0 return new_array Total time: 0.137481 s File: funcs.py Function: tishka_flatten at line 63 Line # Hits Time Per Hit % Time Line Contents ============================================================== 63 @profile 64 def tishka_flatten(data: Iterable) -> List: 65 """ 66 Non recursive algorithm 67 Based on append/extend elements to new list 68 69 """ 70 2 17.0 8.5 0.0 nested = True 71 1012 1044.0 1.0 0.8 while nested: 72 1010 1063.0 1.1 0.8 new = [] 73 1010 992.0 1.0 0.7 nested = False 74 112018 38090.0 0.3 27.7 for i in data: 75 111008 50247.0 0.5 36.5 if isinstance(i, list): 76 1008 1431.0 1.4 1.0 new.extend(i) 77 1008 1138.0 1.1 0.8 nested = True 78 else: 79 110000 42052.0 0.4 30.6 new.append(i) 80 1010 1406.0 1.4 1.0 data = new 81 2 1.0 0.5 0.0 return data Total time: 0.062931 s File: funcs.py Function: zart_flatten at line 84 Line # Hits Time Per Hit % Time Line Contents ============================================================== 84 @profile 85 def zart_flatten(a: Iterable) -> List: 86 """ 87 Non recursive algorithm 88 Based on pop from old and append elements to new list 89 """ 90 2 20.0 10.0 0.0 queue, out = [a], [] 91 21012 12866.0 0.6 20.4 while queue: 92 21010 16849.0 0.8 26.8 elem = queue.pop(-1) 93 21010 17768.0 0.8 28.2 if isinstance(elem, list): 94 1010 1562.0 1.5 2.5 queue.extend(elem) 95 else: 96 20000 13813.0 0.7 21.9 out.append(elem) 97 2 53.0 26.5 0.1 return out[::-1] Total time: 0.052754 s File: funcs.py Function: recursive_flatten_generator at line 100 Line # Hits Time Per Hit % Time Line Contents ============================================================== 100 @profile 101 def recursive_flatten_generator(array: Iterable) -> List: 102 """ 103 Recursive algorithm 104 Looks like recursive_flatten_iterator, but with extend/append 105 106 """ 107 1010 1569.0 1.6 3.0 lst = [] 108 22018 13565.0 0.6 25.7 for i in array: 109 21008 17060.0 0.8 32.3 if isinstance(i, list): 110 1008 6624.0 6.6 12.6 lst.extend(recursive_flatten_generator(i)) 111 else: 112 20000 13622.0 0.7 25.8 lst.append(i) 113 1010 314.0 0.3 0.6 return lst Total time: 0.054103 s File: funcs.py Function: recursive_flatten_iterator at line 116 Line # Hits Time Per Hit % Time Line Contents ============================================================== 116 @profile 117 def recursive_flatten_iterator(arr: Iterable) -> Iterator: 118 """ 119 Recursive algorithm based on iterator 120 Usual solution to this problem 121 122 """ 123 124 22018 20200.0 0.9 37.3 for i in arr: 125 21008 19363.0 0.9 35.8 if isinstance(i, list): 126 1008 6856.0 6.8 12.7 yield from recursive_flatten_iterator(i) 127 else: 128 20000 7684.0 0.4 14.2 yield i Total time: 0.056111 s File: funcs.py Function: tishka_flatten_with_stack at line 131 Line # Hits Time Per Hit % Time Line Contents ============================================================== 131 @profile 132 def tishka_flatten_with_stack(seq: Iterable) -> List: 133 """ 134 Non recursive algorithm 135 Based on zart_flatten, but build on try/except pattern 136 """ 137 2 24.0 12.0 0.0 stack = [iter(seq)] 138 2 5.0 2.5 0.0 new = [] 139 1012 357.0 0.4 0.6 while stack: 140 1010 435.0 0.4 0.8 i = stack.pop() 141 1010 328.0 0.3 0.6 try: 142 1010 330.0 0.3 0.6 while True: 143 22018 17272.0 0.8 30.8 data = next(i) 144 21008 18951.0 0.9 33.8 if isinstance(data, list): 145 1008 997.0 1.0 1.8 stack.append(i) 146 1008 1205.0 1.2 2.1 i = iter(data) 147 else: 148 20000 15413.0 0.8 27.5 new.append(data) 149 1010 425.0 0.4 0.8 except StopIteration: 150 1010 368.0 0.4 0.7 pass 151 2 1.0 0.5 0.0 return new 

рдЧреНрд░рд╛рдлрд┐рдХреНрд╕


рдХреБрд▓ рдорд┐рд▓рд╛рдХрд░ рдкрд░рд┐рдгрд╛рдо:



рд╕рдмрд╕реЗ рдзреАрдореА рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЛ рдЫреЛрдбрд╝рдХрд░, рд╣рдо рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВ:



рднрд╛рдЧ 4. рдирд┐рд╖реНрдХрд░реНрд╖


рд╢рд╛рдпрдж рдореИрдВ рд╕реНрдкрд╖реНрдЯ рдмрд╛рдд рдХрд╣реВрдВрдЧрд╛, рд▓реЗрдХрд┐рди, рдПрд▓реНрдЧреЛрд░рд┐рджрдо рдХреА рдЬреНрдЮрд╛рдд рдЬрдЯрд┐рд▓рддрд╛ рдХреЗ рдмрд╛рд╡рдЬреВрдж, рдкрд░рд┐рдгрд╛рдо рд╕реНрдкрд╖реНрдЯ рдирд╣реАрдВ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рддреЛ, niccolum_flatten рдлрд╝рдВрдХреНрд╢рди, рдЬрд┐рд╕рдХреА рдЬрдЯрд┐рд▓рддрд╛ рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рдереА, рдЕрдВрддрд┐рдо рдЪрд░рдг рдореЗрдВ рдкрд╣реБрдВрдЪ рдЧрдпрд╛ рдФрд░ рдЕрдВрддрд┐рдо рд╕реНрдерд╛рди рд╕реЗ рджреВрд░ рд▓реЗ рдЧрдпрд╛ред рдФрд░ recursive_flatten_generator, recursive_flatten_iterator рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рдмрд╣реБрдд рддреЗрдЬрд╝ рдирд┐рдХрд▓рд╛ред


рд╕рд╛рд░рд╛рдВрд╢рд┐рдд рдХрд░рддреЗ рд╣реБрдП, рдореИрдВ рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ рдпрд╣ рдХрд╣рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдХрд┐ рдпрд╣ рдПрдХ рдЧрд╛рдЗрдб рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рдПрдХ рдЕрдзреНрдпрдпрди рд╕реЗ рдЕрдзрд┐рдХ рдкреНрд░рднрд╛рд╡реА рд╕реВрдЪреА рд▓рд┐рдЦрдиреЗ рд╡рд╛рд▓реЗ рдПрд▓реНрдЧреЛрд░рд┐рджрдо рдХреЛ рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдерд╛ред рдЖрдорддреМрд░ рдкрд░, рдЖрдк рд╕рдмрд╕реЗ рд╕рд░рд▓ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рдмрд┐рдирд╛ рд╕реЛрдЪреЗ рд╕рдордЭреЗ рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдХреНрдпрд╛ рдпрд╣ рд╕рдмрд╕реЗ рддреЗрдЬрд╝ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдереЛрдбрд╝реА рдмрдЪрддред


рдЙрдкрдпреЛрдЧреА рд▓рд┐рдВрдХ


рдЕрдзрд┐рдХ рдкреВрд░реНрдг рдкрд░рд┐рдгрд╛рдо рдпрд╣рд╛рдВ рджреЗрдЦреЗ рдЬрд╛ рд╕рдХрддреЗ рд╣реИрдВред
рдпрд╣рд╛рдВ рдХреЛрдб рдХреЗ рд╕рд╛рде рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА
рдпрд╣рд╛рдБ рд╕реНрдлрд┐рдВрдХреНрд╕ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкреНрд░рд▓реЗрдЦрди


рдпрджрд┐ рдЖрдкрдХреЛ рдХреЛрдИ рддреНрд░реБрдЯрд┐ рдорд┐рд▓рддреА рд╣реИ , рддреЛ рдирд┐рдХреЛрд▓рдо рдЯреЗрд▓реАрдЧреНрд░рд╛рдо рдпрд╛ lastsal@mail.ru рдкрд░ рд▓рд┐рдЦреЗрдВред
рдореБрдЭреЗ рд░рдЪрдирд╛рддреНрдордХ рдЖрд▓реЛрдЪрдирд╛ рдХрд░рдиреЗ рдореЗрдВ рдЦреБрд╢реА рд╣реЛрдЧреАред

Source: https://habr.com/ru/post/hi465531/


All Articles