Односвязный список – такая структура данных, в которой каждый элемент (узел) хранит информацию, а также ссылку на следующий элемент (при этом последний элемент списка ссылается на null).
Рассказ об этой структуре данных мы опустим, если у вас появится желание тщательно разобраться в ней, то добро пожаловать в поисковые системы, ну или можете посмотреть, например, тут: реализация односвязного списка на Си.
И сегодня мы реализуем односвязный список на D…
Вот что реализовано в классе нашего односвязного списка (перечислено в порядке следования соответствующих методов класса):
- создание списка (в двух вариантах);
- проверка списка на пустоту;
- получение длины списка;
- получение головы и хвоста списка;
- получение n-ого элемента;
- получение среза (slice) списка;
- добавление элемента в начало списка;
- добавление элемента в конец списка;
- удаление головы списка;
- удаление хвоста списка;
- удаление n-ого элемента;
- клонирование списка;
- расширение одного списка с помощью другого;
- строковое представление списка;
- преобразование в массив;
- преобразование в ассоциативный массив;
- применение функции к именам узлов списка;
- применение функции к значениям списка.
Реализация представлена ниже:
import std.algorithm; import std.conv; import std.stdio; import std.string; template addProperty(T, string propertyVariableName, string propertyName) { import std.string : format; const char[] addProperty = format( ` private %2$s %1$s; void set%3$s(%2$s %1$s) { this.%1$s = %1$s; } %2$s get%3$s() { return %1$s; } `, propertyVariableName, T.stringof, propertyName ); } class Link(T) { mixin(addProperty!(string, "name", "Name")); mixin(addProperty!(T, "value", "Value")); mixin(addProperty!(Link!T, "next", "Next")); this(T)(string name, T value) { this.name = name; this.value = value; } override string toString() { return format("Link(name=%s, value=%s)", name, to!string(value)); } } class LinkedList(T) { private { Link!T first; } // создание списка static LinkedList!T create(T)(Link!T head) { LinkedList!T list = new LinkedList!T; list.addFront(head.getName, head.getValue); return list; } static LinkedList!T create(T)(Link!T head, Link!T tail) { LinkedList!T list = new LinkedList!T; list.addFront(head.getName, head.getValue); list.addBack(tail.getName, tail.getValue); return list; } this() { first = null; } // пустой ли список ? bool empty() { return (first is null); } // длина списка size_t length() { size_t lengthOfList = 0; auto current = first; while (current !is null) { lengthOfList++; current = current.getNext; } return lengthOfList; } // голова списка LinkedList!T head() { LinkedList!T list = new LinkedList!T; if (first !is null) { list.addFront( first.getName, first.getValue ); } return list; } // хвост списка LinkedList!T tail() { LinkedList!T list = new LinkedList!T; if (first.getNext !is null) { auto current = first.getNext; while (current !is null) { list.addBack( current.getName, current.getValue ); current = current.getNext; } } return list; } // n-ый элемент auto reference(size_t index) { auto current = first; if (index < this.length) { for (int i = 0; i < index; i++) { current = current.getNext; } } return current; } // сечение списка auto slice(size_t begin, size_t end) { LinkedList!T list = new LinkedList!T; if ((0 <= begin) && (end < this.length) && (begin <= end)) { for (size_t i = begin; i < end; i++) { auto element = this.reference(i); list.addBack( element.getName, element.getValue ); } } return list; } // добавление в начало списка void addFront(T)(string name, T value) { Link!T link = new Link!T(name, value); link.setNext(first); first = link; } // добавление в конец списка void addBack(T)(string name, T value) { Link!T link = new Link!T(name, value); auto current = first; auto previous = first; if (first !is null) { while (current !is null) { previous = current; current = current.getNext; } previous.setNext(link); } else { addFront(name, value); } } // удалить голову списка void excludeHead() { first = first.getNext; } // удалить хвост списка void excludeTail() { first.setNext(null); } // удалить элемент по его имени void remove(string name) { auto current = first; auto previous = first; while(current.getName != name) { if (current.getNext !is null) { previous = current; current = current.getNext; } } if (current == first) { first = first.getNext; } else { previous.setNext(current.getNext); } } // копия списка LinkedList!T clone() { LinkedList!T list = new LinkedList!T; auto current = first; while (current !is null) { list.addBack( current.getName, current.getValue ); current = current.getNext; } return list; } // добавить список void extend(T)(LinkedList!T list) { for (size_t i = 0; i < list.length; i++) { auto element = list.reference(i); addBack( element.getName, element.getValue ); } } // строковое представление списка override string toString() { string[] accumulator; auto current = first; while (current !is null) { accumulator ~= current.toString; current = current.getNext; } return format("[%s]", accumulator.join(",")); } // преобразование в массив T[] array() { T[] arrayN; auto current = first; while (current !is null) { arrayN ~= current.getValue; current = current.getNext; } return arrayN; } // преобразование в ассоциативный массив T[string] assocArray() { T[string] arrayN; auto current = first; while (current !is null) { arrayN[current.getName] = current.getValue; current = current.getNext; } return arrayN; } // применение функции к именам узлов списка auto applyToNames(alias Functional)() { LinkedList!T list = new LinkedList!T; auto current = first; while (current !is null) { list.addBack( Functional(current.getName), current.getValue ); current = current.getNext; } return list; } // применение функции к значениям списка auto applyToValues(alias Functional)() { LinkedList!T list = new LinkedList!T; auto current = first; while (current !is null) { list.addBack( current.getName, Functional(current.getValue) ); current = current.getNext; } return list; } }
Вот простая демонстрация всех возможностей:
string func(string s) { return s.toUpper; } void main() { LinkedList!string a = new LinkedList!string; writefln("Empty list: %s", a); a.addBack("A", "Heart"); a.addBack("B", "Desire"); a.addBack("C", "Will"); a.addBack("D", "Rock"); a.addBack("E", "Peace"); a.addBack("F", "One"); a.addBack("G", "by"); a.addBack("H", "Fire"); writefln("Non-Empty list: %s", a); writefln("Head of list: %s", a.head); writefln("Tail of list: %s", a.tail); writefln("Length of list: %s", a.length); writefln("Is empty: %s", a.empty); a.remove("E"); writefln("Remove E: %s", a); a.addFront("0", "TESTING"); writefln("Add 0: %s", a); a.extend(a.clone); writefln("Extending with clone: %s", a); writefln("7th element: %s", a.reference(7u)); writefln("Slice from 3 to 7: %s", a.slice(3u, 7u)); writefln("Applying toLower: %s", a.applyToNames!toLower); writefln("Applying toUpper: %s", a.applyToValues!func); }