Skip to content

Commit

Permalink
add linked lists
Browse files Browse the repository at this point in the history
  • Loading branch information
parrt committed Aug 27, 2017
1 parent c347e47 commit 63785a1
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 6 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,13 @@ You can look at a list of tuples as a list of list too:
Here's how to describe a hashtable with 3 elements in 2 different buckets:

<img src=images/hashtable.png width=350>

If you want the graphviz/dot source, use `source` field of returned `graphviz.files.Source` object.

For 1.1, I added linked lists. Figuring out that layout was annoying. You're welcome. ;)

<img src="images/llist.png" width=500>

Here's an example of specifying lambda functions to extract values from nodes:

<img src="images/tuplellist.png" width=450>
Binary file added images/llist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/tuplellist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
101 changes: 96 additions & 5 deletions lolviz.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def dictviz(d):
return graphviz.Source(s)



def listviz(elems, showassoc=True):
"""
Display a list of elements in a horizontal fashion.
Expand All @@ -49,6 +50,48 @@ def listviz(elems, showassoc=True):
return graphviz.Source(s)


def llistviz(head,
valuefield='value', nextfield='next',
value=None, next=None): # lambda/functions to obtain value/next fields
"""
Display a linked list in a horizontal fashion. The fields/attributes
obtained via getattr() are assumed to be 'value' and 'next' but you
can passing different names, if you like. You can also pass in
lambdas or functions that indicate how to get the node's value or next
fields. The default is to define value to get field valuefield,
similar for next.
"""
if value is None:
value = lambda p : getattr(p,valuefield)
if next is None:
next = lambda p : getattr(p,nextfield)
s = """
digraph G {
nodesep=.05;
rankdir=LR;
ranksep=.2;
node [shape=box, penwidth="0.5",width=.1,height=.1];
"""
p = head
i = 0
edges = []
while p is not None:
html = llist_nodeviz(value(p), valuefield, nextfield)
if next(p) is not None:
edges.append( (i,i+1) )
p = next(p)
s += ' node%d [space="0.0", margin="0.01", fontcolor="#444443", fontname="Helvetica", label=<%s>];\n' % (i,html)
i += 1

# draw edges
for e in edges:
s += 'node%d:next:c -> node%d:value [dir=both, tailclip=false, arrowtail=dot, penwidth="0.5", color="#444443", arrowsize=.4]\n' % e

s += "}\n"
# print s
return graphviz.Source(s)


def lolviz(table, showassoc=True):
"""
Given a list of lists such as:
Expand Down Expand Up @@ -111,7 +154,9 @@ def islol(table):


def elviz(el, showassoc):
if showassoc and type(el) == tuple and len(el) == 2:
if el is None:
els = ' '
elif showassoc and type(el) == tuple and len(el) == 2:
els = "%s&rarr;%s" % (elviz(el[0], showassoc), elviz(el[1], showassoc))
elif type(el)==set:
els = '{'+', '.join([elviz(e, showassoc) for e in el])+'}'
Expand All @@ -124,15 +169,61 @@ def elviz(el, showassoc):
return els


def idx_elviz(idx, el, showassoc):
def label_elviz(label, el, showassoc, port=None):
if port is None:
port = label
return \
"""
<table BORDER="0" CELLBORDER="1" CELLSPACING="0">
<tr>
<td cellspacing="0" bgcolor="#FBFEB0" border="1" sides="b" valign="top"><font color="#444443" point-size="9">%s</font></td>
</tr>
<tr>
<td port="%s" bgcolor="#FBFEB0" border="0" align="center"><font point-size="11">%s</font></td>
</tr>
</table>
""" % (label, port, elviz(el,showassoc))


def llist_nodeviz(nodevalue, value, next):
return \
"""
<table BORDER="0" CELLBORDER="1" CELLSPACING="0">
<tr>
<td cellspacing="0" bgcolor="#FBFEB0" border="1" sides="b" valign="top"><font color="#444443" point-size="9">%d</font></td>
<td cellspacing="0" bgcolor="#FBFEB0" border="1" sides="br" valign="top"><font color="#444443" point-size="9">%s</font></td>
<td cellspacing="0" bgcolor="#FBFEB0" border="1" sides="b" valign="top"><font color="#444443" point-size="9">%s</font></td>
</tr>
<tr>
<td bgcolor="#FBFEB0" border="0" align="center"><font point-size="11">%s</font></td>
<td port="value" bgcolor="#FBFEB0" border="1" sides="r" align="center"><font point-size="11">%s</font></td>
<td port="next" bgcolor="#FBFEB0" border="0" align="center"><font point-size="11">%s</font></td>
</tr>
</table>
""" % (idx, elviz(el,showassoc))
""" % (value,next, elviz(nodevalue,True), ' ')


def idx_elviz(idx, el, showassoc):
return label_elviz(str(idx), el, showassoc)


if __name__ == '__main__':
# test linked list node
class Node:
def __str__(self):
return "(%s,%s)" % (self.value, str(self.next))

def __repr__(self):
return str(self)

def __init__(self, value, next=None):
self.value = value
self.next = next

head = Node('tombu')
head = Node('parrt', head)
head = Node({3,4}, head)
g = llistviz(head)
# or
g = llistviz(head, valuefield='value', nextfield='next')
# or
g = llistviz(head, value=lambda p:p.value, next=lambda p:p.next)
g.render(view=True)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name='lolviz',
version='1.0.2',
version='1.1',
url='https://github.com/parrt/lolviz',
license='BSD',
py_modules=['lolviz'],
Expand Down

0 comments on commit 63785a1

Please sign in to comment.