Русский
Русский
English
Статистика
Реклама

Linkedlist

Hi Programming Language linked list

09.10.2020 00:14:39 | Автор: admin
Продолжаем конструировать язык Hi. Сегодня рассмотрим встроенную реализацию связного списка (linked list).

Связный список >
Связный список представляет цепь элементов узлов, каждый из которых, кроме последнего, связан, то есть имеет ссылку на один следующий элемент (однонаправленный список) и для двунаправленного списка каждый элемент, кроме первого, связан с одним предыдущим элементом.

Не все промышленные языки имеют встроенную поддержку связного списка как структуры данных. Впрочем, его нетрудно реализовать самостоятельно как класс или структуру. Для комфортной работы с релевантными алгоритмами мы добавим встроенные двунаправленные связные списки уже в базовое определение языка Hi.

Для начала создадим экземпляр нашего экспериментального списка:

VAR list = <"I", "will", "be">

В примере выше для трех узлов автоматически создаются двунаправленные ссылки.

Связанный непустой список в языке Hi всегда имеет один узел, который является текущим или активным. По умолчанию это последний добавленный элемент, то есть сейчас это be. Проверим это:

PRINT list.current  # печатает "be"

Добавим в наш список еще два элемента:

list.insert "back", "!"  # теперь list включает элементы: "I", "will", "be", "back", "!"

Встроенный метод insert добавляет новые элементы сразу после активного узла, автоматически выстраивает новые связи и делает текущим последний добавленный элемент.

Удаление осуществляется похожим образом для текущего элемента:

list.remove 1  # list включает элементы: "I", "will", "be", "back"

Впрочем, можно сразу удалить и несколько элементов, для этого нужно предварительно установить указатель на первый удаляемый узел:

VAR secList = listsecList.prev 2secList.remove 2  # secList сейчас включает элементы: "I", "back"

При этом узел, предыдущий перед удаляемым, становится текущим. Если удаляется первый элемент списка, то текущим становится новый первый элемент.

Можно заменить текущий узел на другой, просто присвоив элементу новое значение:

secList.сurrent = "smile"  # secList включает элементы: "smile ", "back"

Удалить все узлы, то есть сделать список пустым можно так:

secList = <>

Переходить на первый и последний узел удобно следующим образом:

list.firstLET last = list.last  # одновременно мы присваиваем новой константе значение активного элемента

Таким образом можно достаточно легко осуществлять разнообразные операции со списком:

PRINT list # печатает "I", "will", "be", "back"list.firstlist.nextlist.insert "not"LET be = list
Подробнее..

Linked List Когда нужно писать свой Copy-on-write в iOS?

01.01.2021 18:11:05 | Автор: admin

Я всегда думал: "А зачем нужно писать свой copy-on-write? Ведь круто, когда есть структуры и они такие быстрые, что всё делают за тебя."

Но все это не нужно, пока не начинаешь писать свои типы и не подсаживаешься на LinkedList'ы.

Что такое этот связанный список и какие у него преимущества?

Связанный список имеет несколько теоретических преимуществ по сравнению с вариантами непрерывного хранения, такими как Array:

  • Связанные списки имеют временную сложность O (1) для вставки первых элементов. Массивы же имеют временную сложность O (n).

  • Соответствие протоколам сбора Swift, таким как Sequence и Collection, предлагает множество полезных методов для довольно небольшого количества требований.

Связанный список (Linked List) - это последовательность элементов данных, в которой каждый элемент называется узлом (Node).

Есть два основных типа связанных списков:

Односвязные списки - это связанные списки, в которых каждый узел имеет ссылку только на следующий узел.

Двусвязные списки - это связанные списки, в которых каждый узел имеет ссылку на предыдущий и следующий узел.

Вам нужно отслеживать, где список начинается и где заканчивается. Обычно это делается с помощью указателей, называемых head и tail.

Вот так это всё выглядит:

public class Node<Value> {  public var value: Value  public var next: Node?      public init(value: Value, next: Node? = nil) {    self.value = value    self.next = next  }}public struct LinkedList<Value> {  public var head: Node<Value>?  public var tail: Node<Value>?    public init() {}  public var isEmpty: Bool { head == nil }}

Вау, прикольная структура данных! Она знает и предыдущий, и следующий элемент. Также в списке мы быстро можем добавить. Но есть проблема.

Так как наша Node'a является классом, а значит и reference type. Это значит, что для многих узлов у нас есть общая ячейка памяти и куча ссылок на них. Они могут расти и с большими размера за ними сложно наблюдать.

Как решить эту проблему? Поскольку LinkedList является структурой и поэтому должен использовать семантику значений. Внедрение своего COW решит эту проблему.

Сделать value type значений с помощью COW довольно просто. Прежде чем изменять содержимое связанного списка, вы хотим создать копию базового хранилища и обновить все ссылки (начальные и конечные) на новую копию.

private mutating func copyNodes() {  guard var oldNode = head else {      return  }  head = Node(value: oldNode.value)  var newNode = head    while let nextOldNode = oldNode.next {    newNode!.next = Node(value: nextOldNode.value)    newNode = newNode!.next    oldNode = nextOldNode  }  tail = newNode}

Этот метод заменит существующие узлы связанного списка вновь выделенными узлами с тем же значением. Здесь всё очень круто, кроме введения накладных расходов O (n) на каждый вызов изменения

Оптимизируем COW

Накладные расходы O (n) при каждом вызове изменения недопустимы.

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

isKnownUniquelyReferenced

В стандартной библиотеке Swift существует функция isKnownUniquelyReferenced. Эта функция может использоваться, чтобы определить, имеет ли объект ровно одну ссылку на него.

И если добавить в начало функции copyNodes() код, то копировать дальше не надо:

private mutating func copyNodes(returningCopyOf node:Node<Value>?) -> Node<Value>? {  guard !isKnownUniquelyReferenced(&head) else {return nil  }  guard var oldNode = head else {return nil}  head = Node(value: oldNode.value)  var newNode = head  var nodeCopy: Node<Value>?  while let nextOldNode = oldNode.next {    if oldNode === node {      nodeCopy = newNode    }    newNode!.next = Node(value: nextOldNode.value)    newNode = newNode!.next    oldNode = nextOldNode}  return nodeCopy}

Этот метод имеет много общего с предыдущей реализацией. Основное отличие состоит в том, что он вернет вновь скопированный узел на основе переданного параметра.

Таким образом Copy-on-write не является чем-то далеким и подкапотным. А вполне управляемым и понятным.

Подробнее..
Категории: Алгоритмы , Swift , Linkedlist

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru