Поиск наименьшего значения во вложенном списке?

Я пытаюсь написать функцию, которая берет список и может печатать наименьшее целое число из этого списка. Теперь я пытаюсь понять, что делать, когда это работает с вложенными списками, и если наименьшее число находится в одном из этих вложенных списков, то в целом он будет печатать это число. Мой код здесь:

def listMin():
list2 = [3,4,[2,99,8],7]

for i in range (len(list2)):
    if type(list2[i]) == type([]):



        y=min(i)
        list2.append(y)
        print "hello"
    if len(list2)== 0:
        return None
    else:


        x= min(list2)
        print x


listMin()

хотя кажется, что он должен печатать номер 2, он этого не делает и просто выдает мне ошибку, как только он достигает вложенного списка, говорящего:

TypeError: 'int' object is not iterable

Я пробовал несколько вещей, но мне трудно понять, почему такие вещи не работают.


person MrPorba    schedule 03.12.2014    source источник


Ответы (4)


Вложение одной глубины

В вашем примере список вложен только один раз. Если это так в целом, то попробуйте:

>>> list2 = [3,4,[2,99,8],7]
>>> min(x if isinstance(x, int) else min(x) for x in list2)
2

Вложенность произвольной глубины

Если допускается более глубокая вложенность, определите эту рекурсивную функцию:

>>> def rmin(lst): return min(x if isinstance(x, int) else rmin(x) for x in lst)
... 

В действии:

>>> rmin(list2)
2

Или, с более глубокой вложенностью:

>>> list3 = [3,4,[[2,99],8],7]
>>> rmin(list3)
2
>>> list4 = [3, 4, [[2, [99, 1]], 8], 7]
>>> rmin(list4)
1

Как это устроено

Функция rmin состоит из одной строки:

return min(x if isinstance(x, int) else rmin(x) for x in lst)

Как видите, это понимание списка, которое просматривает каждое значение x списка lst.

Разделим аргумент min на две части. Первый:

x if isinstance(x, int) else rmin(x)

Это возвращает x, если x является целым числом. В противном случае он вызывает rmin на x. В последнем случае rmin рекурсивно просматривает каждое значение в x и возвращает минимум.

Вторая часть аргумента min:

for x in lst

Это просто обычное понимание списка. Он по очереди извлекает каждое значение из lst и присваивает его x.

person John1024    schedule 03.12.2014

В общем, вы можете сгладить свой список списков и искать min в сглаженном списке. Существует множество рецептов выравнивания. Вот тот, который я взял из здесь.

import collections

def flatten(iterable):
    for el in iterable:
        if isinstance(el, collections.Iterable) and not isinstance(el, str): 
            yield from flatten(el)
        else:
            yield el

list2 = [3,4,[2,99,8],7]

print(list(flatten(list2)))
# [3, 4, 2, 99, 8, 7]
print(min(flatten(list2)))   
# 2

Это будет работать и с несколькими вложенными списками, например:

list2 = [3,4,[2,99,8,[-1,-2]],7]

print(list(flatten(list2)))
# [3, 4, 2, 99, 8, -1, -2, 7]
print(min(flatten(list2)))  
# -2 
person Marcin    schedule 03.12.2014

Проблема вызвана строкой

y=min(i)

где i — целое число, а не список. Вы, вероятно, хотите y = min(list2[i]).

Обратите внимание, что хотя вы добавили этот y обратно в исходный список, цикл не достигнет вновь добавленного элемента, поскольку i будет варьироваться только до исходной длины списка.

С помощью некоторых простых идиом Python ваша первоначальная идея может быть выражена более читабельно следующим образом:

def listMin():
    lst = [3,4,[2,99,8],7]
    for x in lst:
        if type(x) == list:
            lst.append(min(x))
    print min(lst)


listMin()
person YS-L    schedule 03.12.2014
comment
да, но не будет ли это только список в этом утверждении, поскольку оператор if означает, что если тип этого элемента в исходном списке является списком, то найдите минимальное значение этого списка и добавьте его к list2? - person MrPorba; 03.12.2014
comment
list2[i] — это список в этом выражении, да; но i по-прежнему является индексом, который является целым числом. - person YS-L; 03.12.2014

Когда мне нужно было сделать что-то подобное, я написал следующее:

import copy

def nestedMin(list_):
  target_list = copy.deepcopy(list_)  # to keep original list unchanged
  for index in range (len(target_list)):
    if type (target_list[index]) is list:
      target_list[index] = nestedMin(target_list[index])

  return min(target_list)

Я знаю, что это не очень эффективно, продолжает делать глубокое копирование; но это читабельно и работает :)

Пример:

list1 = [2,3,[4, -5, [7, -20]]]
print nestedMin(list1) # prints -20
print list1  # prints [2, 3, [4, -5, [7, -20]]]
person aniliitb10    schedule 08.08.2017