Сегодня еще один урок, в котором будем работать со списками.
Подготовим два списка, один из которых будет заполнен числами, а другой — объектами.
from random import randrange as rnd class ball(): def __init__(self): self.x = 10 self.y = 20 self.r = rnd(10,30) balls = [ball() for x in range(6)] for b in balls: print(b.x, b.y, b.r) print() a = [rnd(10) for x in range(6)] print(a)
Найдем самое большое значение. Начинающие всегда пытаются сравнивать соседние значения, чтобы найти самое большое. Но это тупиковый путь. Представьте что вы находитесь возле 1000 людей, которые стоят в ряд и на вид примерно одинаково весят. Ваша задача — найти самого тяжелого, но у вас нет бумаги и ручки, чтобы записать все значения. Вы можете помнить одно-два значения и у вас есть весы. Какими будут ваши действия?
Прежде, чем читать дальше — подумайте.
Вот, вы подошли к первому… (подумали?)
Взвесили его, запомнили, сколько он весит.
Подошли ко второму, взвесили и его. Если он весит больше, то запомнили его вес, если нет, то, продолжая помнить весь первого, идете дальше. И так обходите всех.
Ключевой момент в том, чтобы в каждый момент времени работать только с одним элементом списка. И еще с одним значением, в котором храниться самое большое из уже просмотренных.
m = a[0] for x in a: if x > m: m = x print(m)
Сделаем тоже самое для списка шариков, найдем самый большой:
m = balls[0].r for b in balls: if b.r > m: m = b.r print(m)
Чтобы найти самый маленький, нужно просто поменять знак.
Посмотрим на встроенную функцию max, которая позволит найти самый большой элемент последовательности:
print(max(a))
Просто и легко, не так ли?
Но что делать с шариками?
На помощь придет прием, который в переводе называется «списковое включение», а иногда «генераторы списков». Термин «генераторы» занят, поэтому я буду использовать «списковое включение», которое я увидел в Р.Сузи в далеком 2006 году. Как я уже говорил, цикл for в Python, вместе со богатыми возможностями по работе со списками, позволяет писать в одну строку вещи, которые потребовали бы 5-6 строк в «обычных языках».
print(max([b.r for b in balls ]))
А теперь узнаем его номер: где стоит самый большой?
im = 0 for i in range(len(a)): if a[i] > a[im]: im = i print(im)
Или так:
print(a.index(max(a)))
В интернете вы можете найти и такой вариант, но зачем нам еще одна переменная?
m = a[0] im = 0 for i in range(len(a)): if a[i] > m: m = a[i] im = i print(im)
А теперь сделаем тоже самое с шариками. На этот раз мы можем перебирать не по номерам, а по значениям:
bm = b for b in balls: if b.r > bm.r: bm = b balls.index(bm)
На самом деле — номер максимального это не самоцель. Обычно с ним нужно что-то делать: изменять, удалять, копировать. В этом случае в числовом списке нужен номер (иначе к элементу мы не обратимся),а в списке объектов достаточно ссылки на объект. Т.е. строка для получения номера, по сути, не нужна, достаточно значения bm, чтобы можно было что-то сделать с шариком, размер которого максимален.
А теперь работа с соседними значениями.
Удалить положительные, которые стоят после отрицательных. Напомню, что удалять проще, перебирая элементы с конца:
a = [rnd(-10,10) for x in range(12)] print(a) old_x = a[-1] for i in range(len(a)-1,0,-1): if a[i] > 0 and a[i-1] < 0: a.pop(i) print(a)
Аналогичная задача для списка шариков (привожу полный код, потому что немного изменил создание списка):
from random import randrange as rnd class ball(): def __init__(self): self.x = 10 self.y = 20 self.r = rnd(-10,10) balls = [ball() for x in range(12)] for b in balls: print(b.r, end = ' ') print() b = balls[-1] for prev_b in balls[::-1]: if prev_b.r < 0 and b.r > 0: balls.remove(b) b = prev_b for b in balls: print(b.r, end = ' ') print()
Никто не мешает работать со списком объектов так же, как мы работаем с числовыми списками — с использованием индекса. Но другой подход, с использованием ссылки на объект дает больше гибкости, потому что индекс может измениться, а ссылка — нет.
Вот, пример: нужно удалить посчитать количество отрицательных и, если их больше, чем положительных, то удалить положительные, иначе — удалить отрицательные.
Мы можем сделать это двумя проходами без особых трудностей:
from random import randrange as rnd class ball(): def __init__(self): self.x = 10 self.y = 20 self.r = rnd(-10,11) balls = [ball() for x in range(12)] for b in balls: print(b.r, end = ' ') print() k_neg = 0 for b in balls: if b.r < 0: k_neg += 1 if k_neg < len(balls) / 2: for b in balls[::-1]: if b.r < 0: balls.remove(b) else: for b in balls[::-1]: if b.r > 0: balls.remove(b) for b in balls: print(b.r, end = ' ') print()
Посмотрим на другое решение:
neg = [] pos = [] for b in balls: if b.r < 0: neg += [b] else: pos += [b] if len(neg) < len(balls) / 2: for killed_b in neg: balls.remove(killed_b) else: for killed_b in pos: balls.remove(killed_b)
Не правда ли, это быстрее читается и больше понятно на человеческий язык, чем на код?
Можем ли мы еще упростить это решение?
Жду ваших предложений в комментариях.
А пока задачи:
1. Найти количество максимальных
2. Удалить все максимальные
3. Удалить все, которые стоят перед максимальнми
4.* Максимальные переставить в начало списка
5. Поменять максимальный и первый
6. Удалить все нули (да-да, опять)
7. Удалить элементы, которые стоят между нулями
8. Все четные удвоить, нечетные — разделить
С списке, заполненном шариками:
class ball(): def __init__(self): self.x = rnd(100,200) self.r = rnd(-10,11) balls = [ball() for x in range(12)]
9. Найти x,y самого большого
10. Самый большой шарик и те,которые ему равны, переместить вправо (x += 10)
11. Самый большой шарик переместить на первое место
12. Самый большой шарик поменять поменять с первым
На следующем занятии будем работать со списком «шариков», после чего — двумерные списки и где-то там будет игра «Сапер» и «Блоки».
Жду комментариев.
Писал карточную игру (выигрывал то у кого сумма номиналов карт на руках, больше) и уперся в проблему определения победителя. Имелся список игроков у каждого по два атрибута: имя, набор карт и по свойству — сумма номиналов, и как вытащить именно номиналы из списка и сравнить их не мог понять. Пробовал создавать для номиналов отдельный список, сортировать их но тогда терялась привязка к владельцу данного набора карт. Прочитав данную главу, нашел решение, даже несколько. Первое похоже на то, как необходимую для сравнения величину мы выдергивали из списка, как в математике общий множитель за скобки. А второй способ когда одной строкой находишь максимальное значение необходимой величины ( вообще красиво). В общем СПАСИБО за красивые и интересные решения! Код не выкладываю так как использовались свои модули.