1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/wizardforcel-lmpythw-zh

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
ex13.md 22 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 06.03.2025 03:52 f9ec0dd

Упражнение 13: Односвязный список

Оригинал: Exercise OnClickListener

Переводчик: Феликс Ли

Лицензия: CC BY-NC-SA 4.0

Гордо использует Google Translate

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

Предупреждение

Эти не являются эффективными реализациями структур данных. Они намеренно сделаны простыми и медленными, чтобы мы могли объяснить измерения и оптимизации в упражнениях 18 и 19. Если вы будете использовать эти структуры данных в производственной работе, возникнут проблемы с производительностью.

Описание

При работе со многими структурами данных в объектно-ориентированных языках программирования (например, Python) вам нужно понимать три общих концепции:

  1. Класс: Это шаблон для создания экземпляров данных.
  2. Экземпляр: Это конкретное значение, созданное на основе класса.
  3. Метод: Это функция, связанная с экземпляром или классом.+ "Узел", обычно контейнер или хранилище данных структуры. Ваше значение хранится здесь.
  • "Ребро", но мы называем его "указатель" или "связь", которая указывает на другие узлы. Все они находятся внутри каждого узла, как правило, в виде экземплярной переменной.

  • "Контроллер", это некоторый класс, знающий, как использовать указатели в узле правильно для построения данных.В Python мы отображаем эти концепции следующим образом:

  • Узел просто объект, определённый классом.

  • Указатель (ребро) просто экземплярная переменная в объекте узла.

  • Контроллер — это ещё один простой класс, который использует узлы для хранения всего и построения данных. Это место всех операций (push, pop, list и так далее), обычно контроллер используется таким образом, что пользователи никогда не работают непосредственно с узлами или указателями.

В некоторых книгах по алгоритмам вы можете видеть такие реализации, где узел и контроллер объединены в одном классе, но это очень запутано и противоречит принципу разделения проблем в дизайне. Лучше отделить узел от контрольного класса, чтобы делать одно дело хорошо и знать, где ошибки.Представьте себе, что мы хотим хранить серию автомобилей. У нас есть первый автомобиль, за которым следует второй, до последнего. Представьте этот список, и мы можем начать представлять дизайн узел/указатель/контроллер:

  • Ноды содержат описание каждого автомобиля. Возможно, это просто переменная node.value класса Car. Если вы ленивы, мы можем называть этот класс SingleLinkedListNode или SLLNode.

  • Каждый SLLNode имеет связь с следующим нодом в списке. Доступ к node.next позволяет получить доступ к следующему автомобилю.

  • Управление осуществляется контроллером, который называется SingleLinkedList, и имеет операции, такие как push, pop, first или count, которые принимают объект типа Car и используют ноды для внутреннего хранения данных. Когда вы добавляете автомобиль методом push в контроллер SingleLinkedList, он обрабатывает его внутри внутреннего списка нод, помещая его в конец списка.

Примечание> Когда в Python есть удобный и быстрый list, зачем нам это делать? Это делается исключительно для изучения структур данных. В реальном мире вы можете использовать list Python и продолжать работу.

Для реализации SingleLinkedListNode нам нужен простой класс, представленный ниже:

class SingleLinkedListNode(object):

    def __init__(self, value, nxt, prev):
        self.value = value
        self.next = nxt

    def __repr__(self):
        nval = self.next and self.next.value or None
        return f"[{self.value}: {repr(nval)}]"

Мы должны использовать слово nxt, так как next является зарезервированным словом в Python. Кроме того, это очень простой пример. Самым сложным является метод __repr__. Когда вы используете %r формат или вызываете repr() на узле, он выводит отладочную информацию. Он должен вернуть строку.

Примечание

Теперь потратите время, чтобы понять, как можно создать список вручную с помощью класса SingleLinkedListNode, а затем пройтись по нему вручную. Это отличная задача на bcm минут, чтобы попрактиковаться.

Контроллер

Как только мы определили наши узлы в классе SingleLinkedListNode, мы точно знаем, что должен делать контроллер. У каждой структуры данных есть набор необходимых операций, которые делают её полезной. Разные операции требуют различных объёмов памяти (пространства) и времени; некоторые дорогие, другие быстрые. Структура SingleLinkedListNode делает некоторые операции очень быстрыми, но многие другие — медленными. В процессе реализации вы узнаете об этом.Самый простой способ просмотреть эти операции — это взглянуть на каркасный вариант класса SingleLinkedList:

class SingleLinkedList(object):

    def __init__(self):
        self.begin = None
        self.end = None

    def push(self, obj):
        """Добавляет новый объект в конец списка."""

    def pop(self):
        """Удаляет последний объект и возвращает его."""

    def shift(self, obj):
        """Добавляет новый объект в начало списка."""

    def unshift(self):
        """Удаляет первый объект и возвращает его."""

    def remove(self, obj):
        """Ищет совпадение объекта и удаляет его."""

    def first(self):
        """Возвращает ссылку на первый объект, не удаляя его."""

    def last(self):
        """Возвращает ссылку на последний объект, не удаляя его."""

    def count(self):
        """Подсчитывает количество объектов в списке."""

    def get(self, index):
        """Получает значение по указанному индексу."""

    def dump(self, mark):
        """Отладочная функция для вывода содержимого связанного списка."""

Для других упражнений я предоставлю вам только указания по реализации этих операций, но в этом упражнении я подробно объясню каждую операцию и как с ними работать, просматривая список функций в SingleLinkedList.

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

from sllist import *

def test_push(): colors = SingleLinkedList() colors.push("Птало Синий") assert colors.count() == 1 colors.push("Ультрамариновый Синий") assert colors.count() == 2

def test_pop(): colors = SingleLinkedList() colors.push("Магента") colors.push("Ализарин") assert colors.pop() == "Ализарин" assert colors.pop() == "Магента" assert colors.pop() == None

def test_unshift(): colors = SingleLinkedList() colors.push("Виридиан") colors.push("Сап Грин") colors.push("Ван Дайк") assert colors.unshift() == "Виридиан" assert colors.unshift() == "Сап Грин" assert colors.unshift() == "Ван Дайк" assert colors.unshift() == None

def test_shift(): colors = SingleLinkedList() colors.shift("Кадмий Оранжевый") assert colors.count() == 1

colors.shift("Карбазол Виолет")
assert colors.count() == 2

assert colors.pop() == "Кадмий Оранжевый"
assert colors.count() == 1
assert colors.pop() == "Карбазол Виолет"
assert colors.count() == 0

def test_remove(): colors = SingleLinkedList() colors.push("Кобальт") colors.push("Цинковый Белый") colors.push("Никель Жёлтый") colors.push("Перинон") assert colors.remove("Кобальт") == 0 colors.dump("до перинона") assert colors.remove("Перинон") == 2 colors.dump("после перинона") assert colors.remove("Никель Жёлтый") == 1 assert colors.remove("Цинковый Белый") == 0

def тест_первый():
    цвета = ОдносвязныйСписок()
    цвета.push("Кадмий Красный Свет")
    assert цвета.first() == "Кадмий Красный Свет"
    цвета.push("Ханса Желтый")
    assert цвета.first() == "Кадмий Красный Свет"
    цвета.shift("Птало Зелёный")
    assert цвета.first() == "Птало Зелёный"
```

Примечание: В данном контексте использована IT-терминология для перевода названий функций и методов.```markdown
def test_last():
    colors = SingleLinkedList()
    colors.push("Кадмий красный свет")
    assert colors.last() == "Кадмий красный свет"
    colors.push("Ханса желтый")
    assert colors.last() == "Ханса желтый"
    colors.shift("Птало зелёный")
    assert colors.last() == "Ханса желтый"
``````python
def test_get():
    colors = SingleLinkedList()
    colors.push("Вермилион")
    assert colors.get(0) == "Вермилион"
    colors.push("Сап грин")
    assert colors.get(0) == "Вермилион"
    assert colors.get(1) == "Сап грин"
    colors.push("Кадмий желтый свет")
    assert colors.get(0) == "Вермилион"
    assert colors.get(1) == "Сап грин"
    assert colors.get(2) == "Кадмий желтый свет"
    assert colors.pop() == "Кадмий желтый свет"
    assert colors.get(0) == "Вермилион"
    assert colors.get(1) == "Сап грин"
    assert colors.get(2) == None
    colors.pop()
    assert colors.get(0) == "Вермилион"
    colors.pop()
    assert colors.get(0) == None
```

Прочитайте этот тест, чтобы лучше понять, как должны работать каждые операции перед тем, как вы начнете писать код. Я бы не рекомендовал вносить все эти изменения за один раз. Вместо этого лучше делать это по частям, создавая небольшие тесты и проверяя, работает ли каждый отдельный участок.

> **Примечание**:

> Здесь, если вы не знакомы с автоматическими тестами, вам может потребоваться просмотреть видео, где я показываю, как это делается.

## Введение в аудит
```Когда вы выполняете каждый тест, вы будете проводить аудит кода для поиска ошибок. В конечном итоге, вы будете отслеживать количество найденных вами ошибок, но сейчас вам нужно выполнить аудит после того, как закончите писать код. Аудит — это процесс, аналогичный тому, который налоговая служба проводит, когда она считает, что вы занижаете свои налоги. Они проходят через каждую сделку, каждый доход, все расходы и анализируют, почему вы так тратите деньги. Аналогично, при код-ревью вы проходите через каждый метод и анализируете все входящие параметры и выходные значения.Для базового аудита вы будете выполнять следующее:

```markdown
- Начните с тестовых случаев. В этом примере мы будем проверять `test_push`.
- Посмотрите первую строчку кода и определите, что вызывается и создаётся. В данном случае это `colors = SingleLinkList()`. Это значит, что мы создаем переменную `colors`, а также вызываем метод `SingleLinkList.__init__`.
- Перейдите в начало метода `__init__`, держа тестовый случай и целевой метод (`__init__`) рядом друг с другом. Подтвердите, что вы сделали это. Затем подтвердите, что вы используете правильные параметры функции с корректными значениями типов данных. В данном случае метод `__init__` принимает только `self`, который должен быть правильного типа.
- Затем войдите внутрь метода `__init__` и проведите проверку строки за строкой, подтверждая каждый вызов функции и каждую переменную аналогичным образом. Количество передаваемых параметров верное? Типы данных верные?
- Для каждого ветвления (инструкций if, циклов for и while) подтвердите, что логика верна и что она правильно обрабатывает все возможные условия внутри себя. Есть ли ошибки в блоках else инструкций if? Может ли цикл завершиться? Затем углубитесь в каждое ветвление, следуя тем же шагам для функций, входящих в ветвление, проверяйте переменные и возвращайтесь назад, чтобы проверить возвращаемое значение.
```+ Когда вы достигнете конца функции или встретите оператор `return`, вернитесь обратно к вызывающему методу `test_push`, чтобы проверить, совпадает ли возвращаемое значение с ожидаемым значением при вызове этого метода. Помните, что вы можете применять этот подход ко всем вызовам внутри метода `__init__`.
+ Наконец, когда вы достигнете конца метода `test_push`, вы завершите процесс и выполните рекурсивную проверку всех вызываемых им методов. Этот процесс может показаться скучным вначале, но вы станете всё быстрее. В видео вы увидите, что я делаю это перед каждым тестом (или хотя бы стараюсь делать это).Процесс состоит из следующих шагов:

* Напишите тестовый код.
* Напишите код, чтобы сделать тесты работоспособными.
* Аудируйте оба этих компонента.
* Выполните тесты, чтобы проверить корректность.

## Упражнение

Мы пришли к этому разделу, и теперь вы готовы попробовать. Прежде всего, просмотрите тесты и исследуйте их работу. Изучите код в `sllist.py`, чтобы понять, что вам нужно сделать. Я рекомендую, когда вы пытаетесь реализовать функцию в `SingleLinkList`, сначала написать комментарии, описывающие её действие, а затем заполнить Python-код, чтобы эти комментарии стали рабочими. Вы увидите, как я это делаю в видео.

После того как вы потратите несколько сессий по 45 минут, чтобы "разобрать" его и попытаться заставить работать, пора смотреть видео. Сначала попробуйте сами, чтобы лучше понять то, что я пытаюсь сделать, что позволит вам лучше воспринять видео. Видео будет просто программированием без слов, но я сделаю голосовое сопровождение, чтобы объяснить происходящее. Видео также будет выполнено быстрее, чтобы экономить время, и я буду урезать любые скучные ошибки или потерянное время.

Как только вы увидите, как я это сделал, сделайте свои заметки (правильно?), а затем попробуйте более строгое испытание и проведите процесс аудита максимально внимательно.## Аудит

После написания кода убедитесь, что вы выполнили аудит согласно процессу, описанному в третьей части. Если вы не уверены, как это сделать, я также проведу аудит этого упражнения в видеоролике.

## Глубокое обучение

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

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/wizardforcel-lmpythw-zh.git
git@api.gitlife.ru:oschina-mirror/wizardforcel-lmpythw-zh.git
oschina-mirror
wizardforcel-lmpythw-zh
wizardforcel-lmpythw-zh
master