Сегодня еще один урок, в котором будем работать со списками.
Подготовим два списка, один из которых будет заполнен числами, а другой — объектами.
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. Самый большой шарик поменять поменять с первым
На следующем занятии будем работать со списком «шариков», после чего — двумерные списки и где-то там будет игра «Сапер» и «Блоки».
Жду комментариев.
Писал карточную игру (выигрывал то у кого сумма номиналов карт на руках, больше) и уперся в проблему определения победителя. Имелся список игроков у каждого по два атрибута: имя, набор карт и по свойству — сумма номиналов, и как вытащить именно номиналы из списка и сравнить их не мог понять. Пробовал создавать для номиналов отдельный список, сортировать их но тогда терялась привязка к владельцу данного набора карт. Прочитав данную главу, нашел решение, даже несколько. Первое похоже на то, как необходимую для сравнения величину мы выдергивали из списка, как в математике общий множитель за скобки. А второй способ когда одной строкой находишь максимальное значение необходимой величины ( вообще красиво). В общем СПАСИБО за красивые и интересные решения! Код не выкладываю так как использовались свои модули.