Use recursion to avoid writing nested loop number equal to the number of layers?

╄→гoц情女王★ 提交于 2020-04-18 05:45:52

问题


This excellent answer to Return a list of all objects vertically stacked above a given object? started me on tree traversal, now I need to start building a complex control panel using wxPython.

I'm learning about sizers but for this abstracted script I've used matplotlib to generate what the panel will look like.

The part I need help with is only the bit near the end with the comments First layer, Second layer and Third layer. What I need is to use recursion so that I don't have to have the correct number of nested loops equal to the number of layers.

Once I get better at wxPython I'll use the same recursion to build the real control panel.

Each heavy black rectangle will ultimately be a cluster of wxPython widgets, and each thing red rectangle will be the enclosing sizer.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.lines as lines

class DG():
    def __init__(self, name):

        self.dgs = []
        self.name = str(name)

    def add_dg(self, name):
        dg = DG(name=name)
        self.dgs.append(dg)
        return dg

    def __repr__(self):
        return ('{self.name}'.format(self=self))

    def find_trees(self):
        # https://stackoverflow.com/questions/60339232/return-a-list-of-all-objects-vertically-stacked-above-a-given-object
        # https://en.wikipedia.org/wiki/Tree_traversal
        self.trees = []
        def __fill_dg_trees(dg_trees, dg, path):
            for th in dg.dgs:
                __fill_dg_trees(dg_trees, th, path + [dg])
            if not dg.dgs:
                self.trees.append(path + [dg])
        __fill_dg_trees(self.trees, self, [])
        self.n_trees = len(self.trees)

class Sub():
    def __init__(self, name):
        self.width = 1.0 
        self.x0 = 0.0 
        self.name = name
        self.dgs = []

    def add_dg(self, name):
        dg = DG(name=name)
        self.dgs.append(dg)
        return dg

    def find_trees(self):
        # https://stackoverflow.com/questions/60339232/return-a-list-of-all-objects-vertically-stacked-above-a-given-object
        # https://en.wikipedia.org/wiki/Tree_traversal
        self.trees = []
        def __fill_dg_trees(dg_trees, dg, path):
            for th in dg.dgs:
                __fill_dg_trees(dg_trees, th, path + [dg])
            if not dg.dgs:
                self.trees.append(path + [dg])
        __fill_dg_trees(self.trees, self, [])
        self.n_trees = len(self.trees)

    def __repr__(self):
        return ('{self.name}'.format(self=self))

#  -----------
# |  C  |  D  |
#  -----------------------------
# |      B    |  F  |  G  |  H  |
#  -----------------------------
# |      A    |     E           |
#  -----------------------------
# |       Substrate             |
#  -----------------------------

sub = Sub(name='My Substrate')
A = sub.add_dg(name='A')
B = A.add_dg(name='B')
C = B.add_dg(name='C')
D = B.add_dg(name='D')
E = sub.add_dg('E')
F = E.add_dg('F')
G = E.add_dg('G')
H = E.add_dg('H')

sub.find_trees()
sub.goodies = set(sum(sub.trees, [])).difference(set([sub]))
for thing in sub.goodies:
    thing.find_trees()
sub.tree_height = max([len(tree) for tree in sub.trees]) - 1
sub.n_trees = len(sub.trees)
sub.n_goodies = len(sub.goodies)
print('sub.tree_height: ', sub.tree_height)
print('sub.n_trees: ', sub.n_trees)
print('sub.n_goodies: ', sub.n_goodies)
print('sub.goodies: ', sub.goodies)

for i, tree in enumerate(sub.trees):
    print(i, tree)

def squareit(thing, nh, dh, dd, hw, hh):
    x0 = thing.x0
    linez, texts = [], []
    print('called thing: ', thing)
    print('thing.width, thing.n_trees: ', thing.width, thing.n_trees)
    for i, dg in enumerate(thing.dgs):
        print('i, dg: ', i, dg)
        print('dg.n_trees: ', dg.n_trees)
        dg.width = float(dg.n_trees) * thing.width / thing.n_trees
        dg.x0 = x0
        print('dg.width: ', dg.width)
        x1, x2 = x0+dd, x0 + dg.width - dd
        y1, y2 = nh*dh + dd, ((nh+1)*dh) - dd
        xx = np.array([x1, x2, x2, x1, x1])
        yy = np.array([y1, y1, y2, y2, y1])
        outline = lines.Line2D(xx, yy, lw=1., color='r', alpha=1.0,
                              transform=fig.transFigure, figure=fig) # https://stackoverflow.com/a/5022412/3904031
        xt, yt = x0+1.5*dd, ((nh+0.5)*dh)-dd
        texts.append((xt, yt, dg.name))

        x1, x2 = x0 + 0.5*dg.width - hw, x0 + 0.5*dg.width + hw
        y1, y2 = ((nh+0.5)*dh) - hh, ((nh+0.5)*dh) + hh
        xx = np.array([x1, x2, x2, x1, x1])
        yy = np.array([y1, y1, y2, y2, y1])
        control_pannel_line = lines.Line2D(xx, yy, lw=3., color='k', alpha=1.0,
                              transform=fig.transFigure, figure=fig) # https://stackoverflow.com/a/5022412/3904031
        linez += [outline, control_pannel_line]
        x0 += dg.width
    return linez, texts

if True:
    fig = plt.figure()
    x0 = 0.
    dd = 0.01
    dh = 0.2
    hw, hh = 0.05, 0.075
    # linez, texts = [], []

    # draw the substrate first
    nh = 0
    x1, x2 = x0+dd, x0 + sub.width - dd
    y1, y2 = nh*dh + dd, ((nh+1)*dh) - dd
    xx = np.array([x1, x2, x2, x1, x1])
    yy = np.array([y1, y1, y2, y2, y1])
    outline = lines.Line2D(xx, yy, lw=1., color='r', alpha=1.0,
                          transform=fig.transFigure, figure=fig)

    xt, yt = x0+1.5*dd, ((nh+0.5)*dh)-dd
    texts = [(xt, yt, sub.name)]

    x1, x2 = x0 + 0.5*sub.width - hw, x0 + 0.5*sub.width + hw
    y1, y2 = ((nh+0.5)*dh) - hh, ((nh+0.5)*dh) + hh
    xx = np.array([x1, x2, x2, x1, x1])
    yy = np.array([y1, y1, y2, y2, y1])
    control_pannel_line = lines.Line2D(xx, yy, lw=3., color='k', alpha=1.0,
                          transform=fig.transFigure, figure=fig)
    linez = [outline, control_pannel_line]

    # now iterate through the whole thing

    # first layer:    
    a, b = squareit(sub, nh=1, dh=dh, dd=dd, hw=hw, hh=hh)
    linez += a
    texts += b

    # second layer:    
    for dg in sub.dgs:
        a, b = squareit(dg, nh=2, dh=dh, dd=dd, hw=hw, hh=hh)
        linez += a
        texts += b

        # third layer:    
        for dgg in dg.dgs:
            a, b = squareit(dgg, nh=3, dh=dh, dd=dd, hw=hw, hh=hh)
            linez += a
            texts += b

    fig.lines.extend(linez) # https://matplotlib.org/3.1.0/gallery/pyplots/fig_x.html

    for (x, y, text) in texts:
        fig.text(x, y, text, fontsize=14)
    plt.show()

回答1:


I use dfs and only use one class Sub (since I think Sub and DG is redundant). Here is the code:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.lines as lines

class Sub():
    def __init__(self, name):
        self.width = 1.0 
        self.x0 = 0.0 
        self.name = name
        self.dgs = []

    def add_dg(self, name):
        dg = Sub(name=name)
        self.dgs.append(dg)
        return dg

    def find_trees(self):
        # https://stackoverflow.com/questions/60339232/return-a-list-of-all-objects-vertically-stacked-above-a-given-object
        # https://en.wikipedia.org/wiki/Tree_traversal
        self.trees = []
        def __fill_dg_trees(dg_trees, dg, path):
            for th in dg.dgs:
                __fill_dg_trees(dg_trees, th, path + [dg])
            if not dg.dgs:
                self.trees.append(path + [dg])
        __fill_dg_trees(self.trees, self, [])
        self.n_trees = len(self.trees)

    def __repr__(self):
        return ('{self.name}'.format(self=self))

#  -----------
# |  C  |  D  |
#  -----------------------------
# |      B    |  F  |  G  |  H  |
#  -----------------------------
# |      A    |     E           |
#  -----------------------------
# |       Substrate             |
#  -----------------------------

sub = Sub(name='My Substrate')
A = sub.add_dg(name='A')
B = A.add_dg(name='B')
C = B.add_dg(name='C')
D = B.add_dg(name='D')
E = sub.add_dg('E')
F = E.add_dg('F')
G = E.add_dg('G')
H = E.add_dg('H')

sub.find_trees()
sub.goodies = set(sum(sub.trees, [])).difference(set([sub]))
for thing in sub.goodies:
    thing.find_trees()
sub.tree_height = max([len(tree) for tree in sub.trees]) - 1
sub.n_trees = len(sub.trees)
sub.n_goodies = len(sub.goodies)
print('sub.tree_height: ', sub.tree_height)
print('sub.n_trees: ', sub.n_trees)
print('sub.n_goodies: ', sub.n_goodies)
print('sub.goodies: ', sub.goodies)

for i, tree in enumerate(sub.trees):
    print(i, tree)

def squareit(thing, nh, dh, dd, hw, hh):
    x0 = thing.x0
    linez, texts = [], []
    print('called thing: ', thing)
    print('thing.width, thing.n_trees: ', thing.width, thing.n_trees)
    for i, dg in enumerate(thing.dgs):
        print('i, dg: ', i, dg)
        print('dg.n_trees: ', dg.n_trees)
        dg.width = float(dg.n_trees) * thing.width / thing.n_trees
        dg.x0 = x0
        print('dg.width: ', dg.width)
        x1, x2 = x0+dd, x0 + dg.width - dd
        y1, y2 = nh*dh + dd, ((nh+1)*dh) - dd
        xx = np.array([x1, x2, x2, x1, x1])
        yy = np.array([y1, y1, y2, y2, y1])
        outline = lines.Line2D(xx, yy, lw=1., color='r', alpha=1.0,
                              transform=fig.transFigure, figure=fig) # https://stackoverflow.com/a/5022412/3904031
        xt, yt = x0+1.5*dd, ((nh+0.5)*dh)-dd
        texts.append((xt, yt, dg.name))

        x1, x2 = x0 + 0.5*dg.width - hw, x0 + 0.5*dg.width + hw
        y1, y2 = ((nh+0.5)*dh) - hh, ((nh+0.5)*dh) + hh
        xx = np.array([x1, x2, x2, x1, x1])
        yy = np.array([y1, y1, y2, y2, y1])
        control_pannel_line = lines.Line2D(xx, yy, lw=3., color='k', alpha=1.0,
                              transform=fig.transFigure, figure=fig) # https://stackoverflow.com/a/5022412/3904031
        linez += [outline, control_pannel_line]
        x0 += dg.width
    return linez, texts

if True:
    fig = plt.figure()
    x0 = 0.
    dd = 0.01
    dh = 0.2
    hw, hh = 0.05, 0.075
    # linez, texts = [], []

    # draw the substrate first
    nh = 0
    x1, x2 = x0+dd, x0 + sub.width - dd
    y1, y2 = nh*dh + dd, ((nh+1)*dh) - dd
    xx = np.array([x1, x2, x2, x1, x1])
    yy = np.array([y1, y1, y2, y2, y1])
    outline = lines.Line2D(xx, yy, lw=1., color='r', alpha=1.0,
                          transform=fig.transFigure, figure=fig)

    xt, yt = x0+1.5*dd, ((nh+0.5)*dh)-dd
    texts = [(xt, yt, sub.name)]

    x1, x2 = x0 + 0.5*sub.width - hw, x0 + 0.5*sub.width + hw
    y1, y2 = ((nh+0.5)*dh) - hh, ((nh+0.5)*dh) + hh
    xx = np.array([x1, x2, x2, x1, x1])
    yy = np.array([y1, y1, y2, y2, y1])
    control_pannel_line = lines.Line2D(xx, yy, lw=3., color='k', alpha=1.0,
                          transform=fig.transFigure, figure=fig)
    linez = [outline, control_pannel_line]

    # Using DFS:
    def dfs(node, nh, linez, texts):
        a, b = squareit(node, nh=nh, dh=dh, dd=dd, hw=hw, hh=hh)
        linez += a
        texts += b
        for child in node.dgs:
            dfs(child, nh+1, linez, texts)
    dfs(sub, nh=1, linez=linez, texts=texts)

    fig.lines.extend(linez) # https://matplotlib.org/3.1.0/gallery/pyplots/fig_x.html

    for (x, y, text) in texts:
        fig.text(x, y, text, fontsize=14)
    plt.show()

Notice the part with comment # Using DFS.

I have tried it on my jupyter and seems to output the same thing as your code. Hope this help!



来源:https://stackoverflow.com/questions/61099365/use-recursion-to-avoid-writing-nested-loop-number-equal-to-the-number-of-layers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!