Skip to content

Commit

Permalink
Change Tree.positions() to Tree.walk()
Browse files Browse the repository at this point in the history
Now the function yields pairs of (path, branch) where path is a
sequence of branch indices to get to the paired branch.

Resolves #74, probably
  • Loading branch information
goodmami committed May 6, 2020
1 parent 8d9a7f6 commit 1707cf4
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 36 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Added

* `penman.tree.Tree.positions()` ([#74])
* `penman.tree.Tree.walk()` ([#74])

### Removed

Expand Down
56 changes: 28 additions & 28 deletions penman/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
Definitions of tree structures.
"""

from typing import Dict, List, Set, Mapping, Any
from typing import Dict, List, Tuple, Set, Mapping, Any, Iterator

from penman.types import (Variable, Branch, Node)


_Step = Tuple[Tuple[int, ...], Branch] # see Tree.walk()


class Tree:
"""
A tree structure.
Expand Down Expand Up @@ -43,26 +46,26 @@ def nodes(self) -> List[Node]:
"""
return _nodes(self.node)

def positions(self, start: int = 1) -> List[str]:
def walk(self) -> Iterator[_Step]:
"""
Return the positions of nodes in the tree.
Positions are paths of dot-separated indices starting from
*start*. For example, using the default *start* value of `1`,
the position of the first node is `'1'` and that of its
branches are `'1.1'`, `'1.2'`, `'1.3'`, etc.
Only positions for node branches are returned, and not for
attribute branches. This is so the output of :meth:`positions`
can be zipped with :meth:`nodes`. The position, however,
increments for all branches, so it is possible to have "gaps"
in the positions (e.g., `'1.1'` followed by `'1.3'`).
Iterate over branches in the tree.
This function yields pairs of (*path*, *branch*) where each
*path* is a tuple of 0-based indices of branches to get to
*branch*. For example, the path (2, 0) is the concept branch
`('/', 'bark-01')` in the tree for the following PENMAN
string, traversing first to the third (index 2) branch of the
top node, then to the first (index 0) branch of that node.
(t / try-01
:ARG0 (d / dog)
:ARG1 (b / bark-01
:ARG0 d))
The (*path*, *branch*) pairs are yielded in depth-first order
of the tree traversal.
"""
node = self.node
if node[0] is None:
return []
else:
return _positions(self.node, str(start), start)
yield from _walk(self.node, ())

def reset_variables(self, fmt='{prefix}{j}') -> None:
"""
Expand Down Expand Up @@ -124,17 +127,14 @@ def _nodes(node: Node) -> List[Node]:
return ns


def _positions(node: Node, path: str, start: int) -> List[str]:
def _walk(node: Node, path: Tuple[int, ...]) -> Iterator[_Step]:
var, branches = node
pos = [path]
i = start
for role, target in branches:
if role == '/':
continue
for i, branch in enumerate(branches):
curpath = path + (i,)
yield (curpath, branch)
_, target = branch
if not is_atomic(target):
pos.extend(_positions(target, f'{path}.{i!s}', start))
i += 1
return pos
yield from _walk(target, curpath)


def _default_variable_prefix(concept: Any) -> Variable:
Expand Down
30 changes: 23 additions & 7 deletions tests/test_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def reentrant():
(':ARG1', ('g', [('/', 'gamma'),
(':ARG0', 'b')]))])


@pytest.fixture
def var_instance():
return ('a', [('/', 'alpha'),
Expand Down Expand Up @@ -55,19 +56,34 @@ def test_nodes(self, one_arg_node, reentrant):
('b', [('/', 'beta')]),
('g', [('/', 'gamma'), (':ARG0', 'b')])]

def test_positions(self, one_arg_node, reentrant):
def test_walk(self, one_arg_node, reentrant):
t = tree.Tree(one_arg_node)
assert t.positions() == ['1', '1.1']
assert t.positions(0) == ['0', '0.0']
assert list(t.walk()) == [
((0,), ('/', 'alpha')),
((1,), (':ARG0', ('b', [('/', 'beta')]))),
((1, 0), ('/', 'beta')),
]

t = tree.Tree(reentrant)
assert t.positions() == ['1', '1.1', '1.2']
assert t.positions(0) == ['0', '0.0', '0.1']
assert list(t.walk()) == [
((0,), ('/', 'alpha')),
((1,), (':ARG0', ('b', [('/', 'beta')]))),
((1, 0), ('/', 'beta')),
((2,), (':ARG1', ('g', [('/', 'gamma'),
(':ARG0', 'b')]))),
((2, 0), ('/', 'gamma')),
((2, 1), (':ARG0', 'b')),
]

t = tree.Tree(('a', [('/', 'alpha'),
(':polarity', '-'),
(':ARG0', ('b', [('/', 'beta')]))]))
assert t.positions() == ['1', '1.2']
assert list(t.walk()) == [
((0,), ('/', 'alpha')),
((1,), (':polarity', '-')),
((2,), (':ARG0', ('b', [('/', 'beta')]))),
((2, 0), ('/', 'beta')),
]

def test_reset_variables(self, one_arg_node, reentrant, var_instance):

Expand Down Expand Up @@ -95,7 +111,7 @@ def _vars(t):
'a0', [('/', 'alpha'),
(':ARG0', ('a1', [('/', 'beta')])),
(':ARG1', ('a2', [('/', 'gamma'),
(':ARG0', 'a1')]))])
(':ARG0', 'a1')]))])

t.reset_variables()
assert _vars(t) == ['a', 'b', 'g']
Expand Down

0 comments on commit 1707cf4

Please sign in to comment.