Recursively mirroring a nested tuple in Python - python

I was trying to write a function that inputs a nested tuple and returns a tuple where all the elements are backwards, including those elements in other tuples (basically mirrors it).
So with this input:
((1, (2, 3)), (4, 5))
It should return:
((5, 4), ((3, 2), 1))
What I tried
def mirror(t):
n = 1
for i in t:
if isinstance(i, tuple):
mirror(i)
if n == len(t):
t = list(t)
t = t[::-1]
t = tuple(t)
n += 1
return t

Maybe I'm missing something, but I think it can be done relatively simply:
def mirror(data):
if not isinstance(data, tuple):
return data
return tuple(map(mirror, reversed(data)))
>>> mirror(((1, (2, 3)), (4, 5)))
((5, 4), ((3, 2), 1))
This applies the mirror function to every element in the tuple, combining them into one new tuple in reverse order.

The trickiness of this problem lies in the fact that tuple objects are immutable. One solution I can think of is recursively building each piece in the final reversed result, and then using itertools to join them together.
from itertools import chain
def mirror(data):
r = []
for t in reversed(data):
if isinstance(t, tuple):
t = mirror(t)
r.append((t, ))
return tuple(chain.from_iterable(r))
>>> mirror(((1, (2, 3)), (4, 5)))
((5, 4), ((3, 2), 1))
Thanks to Chris_Rands for the improvement.
Here's a simpler solution, courtesy PM2 Ring -
def mirror(t):
return tuple(mirror(u) for u in t[::-1]) if isinstance(t, tuple) else t
>>> mirror(((1, (2, 3)), (4, 5)))
((5, 4), ((3, 2), 1))
It builds the result tuple recursively but using a gen comp.

This type of structure, list inside list, is called hierarchical structure, which has property that the whole structure is assembled by small structures which resemble the large structure and are again assembled by even smaller structures.
Imaging a tree with branches resembled the whole tree and leaves at the tips. The first thing is to distinguish branches from leaves. If you see a branch, you treat it as a smaller tree (this naturally forms a recursion). If you see a leave, that means you get to the tip of the structure and you can return it (base case in recursion).
To go from bigger branch to smaller branches (deduction in recursion), there are generally two recursive approaches. The first is as what I did, splitting the branch to left and right and going along each of them. The other way is to map on each branch as what had been done by khelwood.
def mirror(T):
if not isinstance(T, tuple):
return T
elif T == ():
return ()
else:
return mirror(T[1:]) + (mirror(T[0]),)
print(mirror(((1,(2,3)),(4,5))))

Couldn't help myself :)
(This is a joke of course, but has the added benefit of reversing the digits ;)
def rev(s, i, acc):
if i == len(s):
return acc
ps = {'(': ')', ')': '('}
return rev(s, i + 1, s[i] + acc) if not s[i] in ps else rev (s, i + 1, ps[s[i]] + acc)
def funnyMirror(t):
return eval(rev(str(t), 0, ''))
print funnyMirror(((1, (2, 83)), (4, 5))) # ((5, 4), ((38, 2), 1))

Related

Implementation of nested pairs

I'm doing Advent of Code and until this point I had no problem to solve issues on my own until day 18 which got me. I want to solve it on my own but I feel like I can't even start however the task itself is not difficult. It's kinda long to elaborate but the point is the following:
I have to implement nested pairs, so every item of a pair can be an another pair and so on, for example:
[[[[[9,8],1],2],3],4]
[[3,[2,[1,[7,3]]]],[6,[5,[4,[3,2]]]]]
I have to do different operations on these pairs, such as deleting pairs on a very deep level and use its values at higher level. I know Python handles tuples really great but I have yet to find a solution for recursive (?) traversal, deletion and "saving" values from the "deep". It's not a wise solution to delete items during iterating. Shall I use a different approach or some custom data structure for a task like this? I don't need an exact solution just some generic guidance.
I haven't read problem 18 from advent of code, but here is an example answer to your question with a list version and a tuple version.
Imagine I have nested pairs, and I want to write a function that delete all the deepest pairs and replaced them with a single number being the sum of the two numbers in the pair. For example:
input -> output
(1, 2) -> 3
((1, 2), 3) -> (3, 3)
(((1,2), (3,4)), ((5,6), (7,8))) -> ((3, 7), (11, 15))
((((((1, 2), 3), 4), 5), 6), 7) -> (((((3, 3), 4), 5), 6), 7)
Here is a version with immutable tuples, that returns a new tuple:
def updated(p):
result, _ = updated_helper(p)
return result
def updated_helper(p):
if isinstance(p, tuple) and len(p) == 2:
a, b = p
new_a, a_is_number = updated_helper(a)
new_b, b_is_number = updated_helper(b)
if a_is_number and b_is_number:
return a+b, False
else:
return (new_a, new_b), False
else:
return p, True
Here is a version with mutable lists, that returns nothing useful but mutates the list:
def update(p):
if isinstance(p, list) and len(p) == 2:
a, b = p
a_is_number = update(a)
b_is_number = update(b)
if isinstance(a, list) and len(a) == 1:
p[0] = a[0]
if isinstance(b, list) and len(b) == 1:
p[1] = b[0]
if a_is_number and b_is_number:
p[:] = [a+b]
return False
else:
return True
Note how I used a substantive, updated, and a verb, update, to highlight the different logic between these two similar functions. Function update performs an action (modifying a list), whereas updated(p) doesn't perform any action, but is the updated pair.
Testing:
print( updated( (((1,2), (3,4)), ((5,6), (7,8))) ) )
# ((3, 7), (11, 15))
l = [[[1,2], [3,4]], [[5,6], [7,8]]]
update(l)
print(l)
# [[3, 7], [11, 15]]

Trying to understand the double index in python

def countSmaller(self, nums):
def sort(enum):
half = len(enum) / 2
if half:
left, right = sort(enum[:half]), sort(enum[half:])
for i in range(len(enum))[::-1]:
if not right or left and left[-1][1] > right[-1][1]:
smaller[left[-1][0]] += len(right)
enum[i] = left.pop()
else:
enum[i] = right.pop()
return enum
smaller = [0] * len(nums)
sort(list(enumerate(nums)))
return smaller
I am a new python coder so this query!.. In left[-1][1] , I understood [-1] makes me think the last index but what does the second index [1] mean.
The second index does the same as the first but with the nested value.
For exemple:
a = [(1, 2), (2, 3), (3, 4)]
a[-1] # (3, 4)
a[-1][1] # 4
In your example you don't have a list with numbers but enumerate objects converted to lists
sort(list(enumerate(nums)))
It means that you have data like this:
nums = [1, 2, 3, 4, 5]
enum_list = list(enumerate(nums)) # [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
It seems like left is an array containing tuples. I.e. Each element of the array is a tuple.
Ex: left=[(value1oftuple1,value2oftuple1),(value1ofarray2,value2ofarray2)]
In this case left[-1][1] would reference the first value in the last element of the array (value1ofarray2).
I found this by running your code and printing the value of left just before your code calls left[-1][1]. This way you can see what data type left is.

Testing functions returning iterable in python

I'm having difficulties testing python functions that
return an iterable, like functions that are
yielding or functions that simply return an iterable, like return imap(f, some_iter) or return permutations([1,2,3]).
So with the permutations example, I expect the output of the function to be [(1, 2, 3), (1, 3, 2), ...]. So, I start testing my code.
def perm3():
return permutations([1,2,3])
# Lets ignore test framework and such details
def test_perm3():
assertEqual(perm3(), [(1, 2, 3), (1, 3, 2), ...])
This will not work, since perm3() is an iterable, not a
list. So we can fix this particular example.
def test_perm3():
assertEqual(list(perm3()), [(1, 2, 3), (1, 3, 2), ...])
And this works fine. But what if I have nested iterables? That is
iterables yielding iterables? Like say the expressions
product(permutations([1, 2]), permutations([3, 4])). Now this is
probably not useful but it's clear that it will be (once unrolling the
iterators) something like [((1, 2), (3, 4)), ((1, 2), (4, 3)), ...].
However, we can not just wrap list around our result, as that will only
turn iterable<blah> to [iterable<blah>, iterable<blah>, ...]. Well
of course I can do map(list, product(...)), but this only works for a
nesting level of 2.
So, does the python testing community have any solution for the
problems when testing iterables? Naturally some iterables can't
be tested in this way, like if you want an infinite generator, but
still this issue should be common enough for somebody to have thought
about this.
I use KennyTM's assertRecursiveEq:
import unittest
import collections
import itertools
class TestCase(unittest.TestCase):
def assertRecursiveEq(self, first, second, *args, **kwargs):
"""
https://stackoverflow.com/a/3124155/190597 (KennyTM)
"""
if (isinstance(first, collections.Iterable)
and isinstance(second, collections.Iterable)):
for first_, second_ in itertools.izip_longest(
first, second, fillvalue = object()):
self.assertRecursiveEq(first_, second_, *args, **kwargs)
else:
# If first = np.nan and second = np.nan, I want them to
# compare equal. np.isnan raises TypeErrors on some inputs,
# so I use `first != first` as a proxy. I avoid dependency on numpy
# as a bonus.
if not (first != first and second != second):
self.assertAlmostEqual(first, second, *args, **kwargs)
def perm3():
return itertools.permutations([1,2,3])
class Test(TestCase):
def test_perm3(self):
self.assertRecursiveEq(perm3(),
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)])
if __name__ == '__main__':
import sys
sys.argv.insert(1, '--verbose')
unittest.main(argv = sys.argv)
1. If the order of results doesn't matter
Use unittest.assertItemsEqual(). This tests that the items are present in both self and reference, but ignores the order. This works on your example one nested deep example. It also works on a 2-deep example that I concocted.
2. If the order of results matters
I would suggest not ever casting the results of perm3() to a list. Instead, compare the elements directly as you iterate. Here's a test function that will work for your example. I added it to a subclass of unittest.TestCase:
def assertEqualIterables(self, itable1, itable2):
for ival1, ival2 in zip(itable1, itable2):
if "__iter__" in dir(ival1):
self.assertEqualIterables(ival1, ival2)
else:
self.assertEquals(ival1, ival2)
Use it like:
def test_perm3(self):
reference = [((1, 2), (3, 4)), ((1, 2), (4, 3)),
((2, 1), (3, 4)), ((2, 1), (4, 3)),]
self.assertEqualIterables(perm3(), reference)
You could extend you suggestion to include type (that was allowing you to distinguish between lists, tuples, etc.), like so:
def unroll(item):
if "__iter__" in dir(item):
return map(unroll, item), type(item)
else:
return item, type(item)
For example:
got = unroll(permutations([1,2]))
([([(1, <type 'int'>), (2, <type 'int'>)], <type 'tuple'>), ([(2, <type 'int'>), (1, <type 'int'>)], <type 'tuple'>)], <type 'itertools.permutations'>)
# note the final: <type 'itertools.permutations'>
expected = [(1, 2), (2, 1)]
assertEqual(x[0], unroll(expected) ) # check underlying
assertEqual(x[1], type(permutations([]) ) # check type
.
One thing to mention, type is coarse in distinguishing between objects e.g. <type 'classobj'>...
I don't know of any standard way python programmers test iterables, but you
can simply apply your idea of map and list into a recursive function
working for any level of nestedness.
def unroll(item):
if "__iter__" in dir(item):
return map(unroll, item)
else:
return item
Then your test will actually work.
def test_product_perms():
got = unroll(product(...))
expected = [[[1, 2], [3, 4]], [[1, 2], [4, 3]], ...]
assertEqual(got, expected)
However there is a flaw with this as you can see. When unrolling something, it
will always be turned to an array, this was desireable for iterables but it
also applies to the tuples. So therfore I had to manually convert the tuples in the expected result to lists. Hence, you can't diffrentiate if outputs are lists or tuples.
Another problem with this naive approach is that a passing test doesn't mean
that the function work. Say that you check assertEqual(list(my_fun()), [1, 2,
3]), while you think it might return an iterable that when "listed" is
equal to [1, 2, 3]. It might be that it did not return an iterable as you
wanted, it might have returned a list or a tuple too!

Sort list by nested tuple values

Is there a better way to sort a list by a nested tuple values than writing an itemgetter alternative that extracts the nested tuple value:
def deep_get(*idx):
def g(t):
for i in idx: t = t[i]
return t
return g
>>> l = [((2,1), 1),((1,3), 1),((3,6), 1),((4,5), 2)]
>>> sorted(l, key=deep_get(0,0))
[((1, 3), 1), ((2, 1), 1), ((3, 6), 1), ((4, 5), 2)]
>>> sorted(l, key=deep_get(0,1))
[((2, 1), 1), ((1, 3), 1), ((4, 5), 2), ((3, 6), 1)]
I thought about using compose, but that's not in the standard library:
sorted(l, key=compose(itemgetter(1), itemgetter(0))
Is there something I missed in the libs that would make this code nicer?
The implementation should work reasonably with 100k items.
Context: I would like to sort a dictionary of items that are a histogram. The keys are a tuples (a,b) and the value is the count. In the end the items should be sorted by count descending, a and b. An alternative is to flatten the tuple and use the itemgetter directly but this way a lot of tuples will be generated.
Yes, you could just use a key=lambda x: x[0][1]
Your approach is quite good, given the data structure that you have.
Another approach would be to use another structure.
If you want speed, the de-factor standard NumPy is the way to go. Its job is to efficiently handle large arrays. It even has some nice sorting routines for arrays like yours. Here is how you would write your sort over the counts, and then over (a, b):
>>> arr = numpy.array([((2,1), 1),((1,3), 1),((3,6), 1),((4,5), 2)],
dtype=[('pos', [('a', int), ('b', int)]), ('count', int)])
>>> print numpy.sort(arr, order=['count', 'pos'])
[((1, 3), 1) ((2, 1), 1) ((3, 6), 1) ((4, 5), 2)]
This is very fast (it's implemented in C).
If you want to stick with standard Python, a list containing (count, a, b) tuples would automatically get sorted in the way you want by Python (which uses lexicographic order on tuples).
I compared two similar solutions. The first one uses a simple lambda:
def sort_one(d):
result = d.items()
result.sort(key=lambda x: (-x[1], x[0]))
return result
Note the minus on x[1], because you want the sort to be descending on count.
The second one takes advantage of the fact that sort in Python is stable. First, we sort by (a, b) (ascending). Then we sort by count, descending:
def sort_two(d):
result = d.items()
result.sort()
result.sort(key=itemgetter(1), reverse=True)
return result
The first one is 10-20% faster (both on small and large datasets), and both complete under 0.5sec on my Q6600 (one core used) for 100k items. So avoiding the creation of tuples doesn't seem to help much.
This might be a little faster version of your approach:
l = [((2,1), 1), ((1,3), 1), ((3,6), 1), ((4,5), 2)]
def deep_get(*idx):
def g(t):
return reduce(lambda t, i: t[i], idx, t)
return g
>>> sorted(l, key=deep_get(0,1))
[((2, 1), 1), ((1, 3), 1), ((4, 5), 2), ((3, 6), 1)]
Which could be shortened to:
def deep_get(*idx):
return lambda t: reduce(lambda t, i: t[i], idx, t)
or even just simply written-out:
sorted(l, key=lambda t: reduce(lambda t, i: t[i], (0,1), t))

Return a sequence of a variable length whose summation is equal to a given integer

In the form f(x,y,z) where x is a given integer sum, y is the minimum length of the sequence, and z is the maximum length of the sequence. But for now let's pretend we're dealing with a sequence of a fixed length, because it will take me a long time to write the question otherwise.
So our function is f(x,r) where x is a given integer sum and r is the length of a sequence in the list of possible sequences.
For x = 10, and r = 2, these are the possible combinations:
1 + 9
2 + 8
3 + 7
4 + 6
5 + 5
Let's store that in Python as a list of pairs:
[(1,9), (2,8), (3,7), (4,6), (5,5)]
So usage looks like:
>>> f(10,2)
[(1,9), (2,8), (3,7), (4,6), (5,5)]
Back to the original question, where a sequence is return for each length in the range (y,x). I the form f(x,y,z), defined earlier, and leaving out sequences of length 1 (where y-z == 0), this would look like:
>>> f(10,1,3)
[{1: [(1,9), (2,8), (3,7), (4,6), (5,5)],
2: [(1,1,8), (1,2,7), (1,3,6) ... (2,4,4) ...],
3: [(1,1,1,7) ...]}]
So the output is a list of dictionaries where the value is a list of pairs. Not exactly optimal.
So my questions are:
Is there a library that handles this already?
If not, can someone help me write both of the functions I mentioned? (fixed sequence length first)?
Because of the huge gaps in my knowledge of fairly trivial math, could you ignore my approach to integer storage and use whatever structure the makes the most sense?
Sorry about all of these arithmetic questions today. Thanks!
The itertools module will definately be helpful as we're dealing with premutations - however, this looks suspiciously like a homework task...
Edit: Looks like fun though, so I'll do an attempt.
Edit 2: This what you want?
from itertools import combinations_with_replacement
from pprint import pprint
f = lambda target_sum, length: [sequence for sequence in combinations_with_replacement(range(1, target_sum+1), length) if sum(sequence) == target_sum]
def f2(target_sum, min_length, max_length):
sequences = {}
for length in range(min_length, max_length + 1):
sequence = f(target_sum, length)
if len(sequence):
sequences[length] = sequence
return sequences
if __name__ == "__main__":
print("f(10,2):")
print(f(10,2))
print()
print("f(10,1,3)")
pprint(f2(10,1,3))
Output:
f(10,2):
[(1, 9), (2, 8), (3, 7), (4, 6), (5, 5)]
f(10,1,3)
{1: [(10,)],
2: [(1, 9), (2, 8), (3, 7), (4, 6), (5, 5)],
3: [(1, 1, 8),
(1, 2, 7),
(1, 3, 6),
(1, 4, 5),
(2, 2, 6),
(2, 3, 5),
(2, 4, 4),
(3, 3, 4)]}
The problem is known as Integer Partitions, and has been widely studied.
Here you can find a paper comparing the performance of several algorithms (and proposing a particular one), but there are a lot of references all over the Net.
I just wrote a recursive generator function, you should figure out how to get a list out of it yourself...
def f(x,y):
if y == 1:
yield (x, )
elif y > 1:
for head in range(1, x-y+2):
for tail in f(x-head, y-1):
yield tuple([head] + list(tail))
def f2(x,y,z):
for u in range(y, z+1):
for v in f(x, u):
yield v
EDIT: I just see it is not exactly what you wanted, my version also generates duplicates where only the ordering differs. But you can simply filter them out by ordering all results and check for duplicate tuples.

Categories