-1

This is only for self learning of the concept and might not have practical use

My question is

  1. can I use only recursive function and list comprehension to flatten a unknown level of nested list?

  2. If 1 is possible, can I only use list comprehension + lambda function to get the same purpose?

So far this all I can get but it seems not working.

l=[1,[2,3],[4,5,[6,7,8,9]]] # assuming unknown level of nesting

def fun_d(x):
    return [fun_d(e) if isinstance(e,list) else e for e in x]

fun_d(l)

Out[25]: [1, [2, 3], [4, 5, [6, 7, 8, 9]]]
xappppp
  • 481
  • 7
  • 18
  • @dawg not sure this was a good duplicate as it is only one level deep, for which `itertools.chain.from_iterable()` is a trivial solution. I wouldn't doubt there is probably a more appropriate duplicate. – AChampion May 02 '18 at 04:48
  • @AChampion you're right – juanpa.arrivillaga May 02 '18 at 04:52
  • There are many good solutions to [Flatten an irregular list of lists](https://stackoverflow.com/questions/2158395/flatten-an-irregular-list-of-lists/2158532#2158532) but not specific to using recursive list comprehensions. – AChampion May 02 '18 at 04:52
  • Consider then [this](https://stackoverflow.com/q/44417443/298607) as a dup – dawg May 02 '18 at 05:20
  • Or [this](https://stackoverflow.com/q/17338913/298607) as a dup... – dawg May 02 '18 at 05:22
  • @dawg both of those are just one-level deep flattens, the complexity added here is arbitrary nesting. As purely concept learning, fine - would never suggest this in practice. – AChampion May 02 '18 at 05:26
  • `[[1, 2, 3], [4, 5, 6], [7, 8], [9], 10, 11]` is not one level – dawg May 02 '18 at 05:30
  • @dawg, Sorry, but I would define that as only 1 level of nesting. As in max depth of list within lists = 1. OP's example was a depth of 2 but asked for arbitrary depth. The dups you link to only solve for depth = 1. – AChampion May 02 '18 at 05:34

1 Answers1

7

You can though it is a little strange:

def fun_d(x):
    return [i for e in x for i in (fun_d(e) if isinstance(e,list) else [e])]

In[] :
l=[1,[2,3],[4,5,[6,7,8,9]]]
fun_d(l)

Out[]:
[1, 2, 3, 4, 5, 6, 7, 8, 9]

You may want to use Sequence rather than list so other types of sequences would also be flattened.

from typing import Sequence

def fun_d(x):
    return [i for e in x for i in (fun_d(e) if isinstance(e, Sequence) and not isinstance(e, str) else [e])]

A named lambda is trivial, for a truly anonymous lambda you can use a y-combinator

And just to show how ridiculous this is, an anonymous recursive lambda:

In []:
lis = [1,[2,3],[4,5,[6,[7,8],9]]]
(lambda f: f(f))(lambda f: (lambda fun_d: lambda x: [i for e in x for i in (fun_d(e) if isinstance(e, list) else [e])])(lambda x: f(f)(x)))(lis)

Out[]:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
AChampion
  • 29,683
  • 4
  • 59
  • 75
  • 3
    I don't believe I did, as this is recursive and your example is various levels of depth. – AChampion May 02 '18 at 04:41
  • I would say the second example is closest to what I expected. But you are right, it is not elegant at all. – xappppp May 02 '18 at 11:57
  • @AChampion, hats off to your lambda solution. I'd be studying that for a while. – Xero Smith May 03 '18 at 02:53
  • @AChampion can you shed more lights on anounymous $lambda$? Is that result in more simpler/intuitive expression? – xappppp May 06 '18 at 20:51
  • 1
    It's a [`y-combinator`](https://en.wikipedia.org/wiki/Fixed-point_combinator#Fixed_point_combinators_in_lambda_calculus). There isn't a simpler way to define an anonymous recursive function but this is trivial in python when you can add variable names, e.g.: `fun_d = lambda x: [i for e in x for i in (fun_d(e) if isinstance(e,list) else [e])]; fun_d(lis)` – AChampion May 06 '18 at 20:58
  • @AChampion, great solution!, could you please point me to some good source to understand your second solution(y-combinator!). I know how to use lambda functions but didn't get a clear understanding of your solution as there is too many nesting in the code(is it possible for you write the same in multiple lines for quick understanding?)! – Anu Jan 12 '19 at 18:02
  • @AChampion, Also, I tried to run your solution2 on another sequence & it doesn't work, `A = (1,(2,3),(4,5,(6,(7,8),9)))` another, `A = ['image1', [[[[[4,5,[6,[7,8],9]]]]]], [[('image3', 'image3.1')], 'image4'], 'image5', (4,5,(6,7,8,9))]`, but it works for `A = [1,[2,3],[4,5,[6,[7,8],9]]]` , `A = ['image1', [[[['image2']]]], [['image3'], 'image4'], 'image5']` and `A= 'xyzzy'`. Any suggestion why it's not working on the first two inputs? – Anu Jan 12 '19 at 18:20
  • 1
    @anu you are using tuples not a list, as I mentioned test for `typing.Sequence` instead of a `list` for a more general solution, e.g. `from typing import Sequence` then `... isinstance(e, Sequence) ...` – AChampion Jan 12 '19 at 18:44
  • @AChampion, Great! I got it! Using `typing.Sequence` it works for both sequences `A = (1,(2,3),(4,5,(6,(7,8),9)))` and `A = [1, [[[[2]]]], [[3], 4], 5]`, but it given an error `RecursionError: maximum recursion depth exceeded in comparison` on input `A = ['str1', [[[['str2']]]], [['str3'], 'str4'], 'str5']` do you know the reason and how to fix this issue? – Anu Jan 12 '19 at 19:26
  • 1
    Strings will create problems with `Sequence` because they are considered a sequence, even a single character string, so you will need to guard against strings, e.g. `if isinstance(e, Sequence) and not isinstance(e, str)` you would have the same issue with `bytes` if you use them. – AChampion Jan 12 '19 at 19:43
  • @AChampion, gotcha!., but if I use simple `list` instead of `typing.Sequence` it `doesn't give recursion error!` and flatten the above list too. Any suggestion on that? – Anu Jan 12 '19 at 19:56
  • If you use `list` it would not flatten `tuple`s, but that is your choice. You can explicitly test for `list`, `tuple` and any other sequences you want to support or you can support any `Sequence` and just exclude `str`. – AChampion Mar 12 '19 at 20:53