UNPKG

cstd

Version:

CoffeeScript Standart Library

589 lines (554 loc) 20.9 kB
# Тут создаётся список на основе узлов {Node} = require './node.coffee' {CSObject} = require './object.coffee' nn = new Node null nn.n = nn # Список на основе узлов `Node`. Есть внутренний указатель, # который перемещается методами `#next()`, `#prev()`, # `#begin()`, `#end()` и значение получается всегда через # метод `#get()`. # # В результате вставок указатель всегда # указывает на тот же самый элемент, а индекс указателя `#index` # даптивно подстраивается. То же самое по возможности и при # изъятии узла, но если изымается элемент, на котором стоит указатель, # тогда он смещается к ближайшему узлу в сторону начала, а если # это не возможно, то ближайшему узлу в сторону конца. class List extends CSObject # @property {Node} начало очереди для взятия O(1) _f: nn # @property {Node} конец очереди для добавления O(1) _b: nn # @property {Node} Указатель на текущий элемент _p: nn # @property {Number} Индекс текущего указателя, # если -1, то значит список пуст index: -1 # @property {Number} длина очереди # @read-only length: 0 # Создаёт пустой список, устанавливает значения # @throw {TypeError} при наличии аргументов constructor: -> if arguments.length isnt 0 throw new TypeError "List constructor haven't arguments" @_f = new Node null @_p = @_b = @_f @index = -1 # Добавление _одного_ элемента в список в конец # @param {*} some сущность для добавления в конец # @throw {TypeError} если сущности нет или она не одна # @return {List} `this` pushBack: (some) -> if arguments.length > 1 throw new TypeError "one pushBack, one element" if arguments.length is 0 throw new TypeError "pushBack without value" if @length++ isnt 0 b = @_b @_b = (b.n = new Node some, null, b) else @_f.v = some @index = 0 @_p = @_f @ # Добавление одной сущности `some` в начало списка. # При этом указатль остаётся на том же элементе, где и был, # а в случае пустого индекса указывает на новый узел. # Индекс же следит за указателем всегда. # @param {*} some сущность для вставки # @throw {TypeError} если сущности нет или она не одна # @return {List} `this` pushFront: (some) -> if arguments.length > 1 throw new TypeError "one pushFront, one element" if arguments.length is 0 throw new TypeError "pushFront without value" if @length++ isnt 0 f = @_f @_f = (f.p = new Node some, f) @index++ else @_f.v = some @index = 0 @_p = @_f @ # Взятие первого элемента из очереди. Указатель не смещается, # если не удалён элемент указателя. Если же удалён, то он # переходит на предыдущий элемент. # @throw {Error} если список пуст # @throw {TypeError} если переданы аргументы # @return {*} some значение первого узла popFront: -> if @length is 0 throw new Error "list is empty" if arguments.length isnt 0 throw new TypeError "popFront haven't arguments" l = @length-- node = @_f some = node.v if l isnt 1 if @_p is @_f @_p = @_f = node.n else @_f = node.n @index-- node.destructor() @_f.p = null else @index = -1 @_p = node node.v = null some # Изымает и возвращает последнее значение. # @throw {Error} если список пуст # @throw {TypeError} если переданы аргументы # @return {*} some значение последнего узла popBack: -> if @length is 0 throw new Error "list is empty" if arguments.length isnt 0 throw new TypeError "popBack haven't arguments" l = @length-- node = @_b some = node.v if l isnt 1 if @_p is @_b @_p = @_b = node.p @index-- else @_b = node.p node.destructor() @_b.n = null else @index = -1 @_p = node node.v = null some # Присоединяет сущность `some` после указателя. # @param {*} some вставляемое значение # @throw {TypeError} если нет сущности или она не одна # @return {List} `this` append: (some) -> if arguments.length > 1 throw new TypeError "more than one append" if arguments.length is 0 throw new TypeError "append nothing" if @length++ isnt 0 n = @_p.n if n isnt null n.p = @_p.n = new Node some, n, @_p else @_b = @_p.n = new Node some, null, @_p else @index = 0 @_p = @_b = @_f @_f.v = some @ # Присоединяет сущность `some` перед указателем. # @param {*} some вставляемое значение # @throw {TypeError} если нет сущности или она не одна # @return {List} `this` prepend: (some) -> if arguments.length > 1 throw new TypeError "more than one prepend" if arguments.length is 0 throw new TypeError "prepend nothing" if @length++ isnt 0 p = @_p.p if p isnt null p.n = @_p.p = new Node some, @_p, p else @_f = @_p.p = new Node some, @_p, null @index++ else @index = 0 @_p = @_b = @_f @_f.v = some @ # Присоединяет сущности `some` в самый конец списка # @param {*...} some... добавляемые значения подряд через зяпятую # @throw {TypeError} если нет ни одной сущности # @return {List} `this` add: (first, list...) -> if arguments.length is 0 throw new TypeError "add without value" p = @_b if @length is 0 @_f.v = first @length = 1 @index = 0 else p = (p.n = new Node first, null, p) @length++ for item in list p = p.n = new Node item, null, p @_b = p @length += list.length @ # Перемещает указатель на `step` шагов относительно текущего положения. # Если `step` < 0 тогда двигаемся к началу, а иначе к концу списка. # # *Если шаги выходят за рамки, то возвращается null.* # @param {Number} step на сколько шагов идём и в какую сторону # @throw {Error} если не указан `step` или аргументов больше 1 # @throw {TypeError} если шаги указаны не числом # @return {List|null} `this` или признак выхода за границы go: (step) -> if arguments.length is 0 throw new Error "`step` is required" if typeof step isnt 'number' throw new TypeError "`step` must be a Number" if arguments.length > 1 throw new Error "too many arguments" unless 0 <= @index + step <= @length - 1 return null return @ if step is 0 currentIndex = 0 currentPointer = @_p @index += step if step > 0 while currentIndex++ < step currentPointer = currentPointer.n else while currentIndex-- > step currentPointer = currentPointer.p @_p = currentPointer @ # То же, что и `#go(1)` # @throw {Error} если переданы аргументы # @return {List|null} `this` или признак, что указатель уже в конце next: -> if arguments.length > 0 throw new Error "next haven't arguments" if @index is @length - 1 return null @_p = @_p.n @index++ @ # То же, что и `#go(-1)` # @throw {Error} если переданы аргументы # @return {List|null} `this` или признак, что указатель уже в начале prev: -> if arguments.length > 0 throw new Error "prev haven't arguments" if @index <= 0 return null @_p = @_p.p @index-- @ # Перемещает указатель на самый _первый_ узел # @throw {Error} если переданы аргументы # @return {List|null} `this` или признак пустого списка begin: -> if arguments.length > 0 throw new Error "begin haven't arguments" return null if @length is 0 @_p = @_f @index = 0 @ # Перемещает указатель на самый _последний_ узел # @throw {Error} если переданы аргументы # @return {List|null} `this` или признак пустого списка end: -> if arguments.length > 0 throw new Error "end haven't arguments" return null if @length is 0 @_p = @_b @index = @length - 1 @ # Возвращает значение первого узла без смещения указателя # и без изменений списка. # @throw {Error} если список пуст или переданы аругменты # @return {*} значение первого узла getFront: -> if arguments.length > 0 throw new Error "getFront haven't arguments" if @length is 0 throw new Error "list is empty" @_f.v # Возвращает значение последнего узла без смещения указателя # и без изменения списка. # @throw {Error} если список пуст или переданы аругменты # @return {*} значение последнего узла getBack: -> if arguments.length > 0 throw new Error "getBack haven't arguments" if @length is 0 throw new Error "list is empty" @_b.v # Возвращает значение узла "под" указателем, не меняя список # @throw {Error} если список пуст или переданы аругменты # @return {*} значение узла указателя get: -> if arguments.length > 0 throw new Error "get haven't arguments" if @length is 0 throw new Error "list is empty" @_p.v # Возвразает сам *узел* указателя # @throw {Error} если список пуст или переданы аругменты # @return {*} узел указателя currentNode: -> if arguments.length > 0 throw new Error "currentNode haven't arguments" if @length is 0 throw new Error "list is empty" @_p # Переставляет местами _значения_ узлов # (след./пред. относительно указателя). Если `rev` не равен `true`, # томеняемся со следующим узлом от указателя, иначе -- с прудыдущим. # @throw {Error} если в списке менее 2-ух элементов # @throw {Error} если пытаемся махнуться со слледующим в конце # @throw {Error} если пытаемся махнуться с предыдущим в начале # @return {List} `this` swap: (rev) -> if @length < 2 throw new Error "for swap require 2 nodes minimum" rev = no if rev isnt yes unless rev if @_p.n is null throw new Error "it is last element, swap impossible" v = @_p.n.v @_p.n.v = @_p.v @_p.v = v v = null return @ if @index is 0 throw new Error "it is first element, reverse swap impossible" v = @_p.p.v @_p.p.v = @_p.v @_p.v = v v = null return @ # Устанавливает значение указателя на `some` # @throw {Error} если список пуст или аргументов не 1 # @return {List} `this` set: (some) -> if (l = arguments.length) is 0 throw new Error "can't set nothing to value" if @length is 0 throw new Error "can't set to empty list" if l > 1 throw new Error "too many arguments" @_p.v = some @ # Удаляет элемент или несколько из списка, начиная # с позиции `start` и до позиции `end` (от начала), # если `returnNodes` равна `true`, то удалённые узлы # вернутся в виде массива из метода. # Если `end` не установлено, то удаляется только элемент # с индексом `start` # @overload erase(start, end, returnNodes) # Удаление диапазона от `start` до `end` включительно # @param {Number} start начальная позиция для удаления # @param {Number} end последняя позиция # @param {Boolean} returnNodes возвращать ли узлы [default = no] # @throw {RangeError} если `start` < 0 или `end` >= `#length` # @throw {RangeError} если `start` > `end` # @throw {TypeError} если аргументов многовато # @throw {TypeError} если типы аргументов не правильные # @return {List|Array<Node>} `this` или массив удалённых узлов, # если `returnNodes` равен `yes` # @overload erase(start, returnNodes) # Удаление диапазона от `start` до `end` включительно # @param {Number} start начальная позиция для удаления # @param {Boolean} returnNodes возвращать ли узлы [default = no] # @throw {RangeError} если `start` < 0 # @throw {TypeError} если аргументов многовато # @throw {TypeError} если типы аргументов не правильные # @return {List|Array<Node>} `this` или массив удалённых узлов, # если `returnNodes` равен `yes` erase: (start, end, returnNodes) -> l = arguments.length listLength = @length dual = no switch l when 0 start = @index when 1 if typeof start is 'boolean' start = @index returnNodes = start when 2 if typeof end is 'boolean' returnNodes = end end = start else dual = yes returnNodes = yes when 3 dual = yes returnNodes ?= yes if typeof start isnt 'number' and typeof start isnt 'boolean' throw new TypeError "`start` must be a number or boolean" if l is 2 and typeof end isnt 'number' and typeof start isnt 'boolean' throw new TypeError "`end` must be a number or boolean" if l is 3 if typeof end isnt 'number' throw new TypeError "`end` must be a number" if typeof returnNodes isnt 'boolean' throw new TypeError "`returnNodes` must be a boolean" if l > 3 throw new Error "too many arguments" if listLength is 0 throw new RangeError "list is empty" if start < 0 throw new RangeError "`start` less than 0" if start >= listLength throw new RangeError "`start` more than last index" if typeof end is 'number' if end < 1 end = listLength + end - 1 if end < start throw new RangeError "`end` less than index of `start`" if end >= listLength throw new RangeError "`end` more than last index" else i = @index @length-- unless dual if start is i # если на том. на котором стоим,то n = @_p if i > 0 # при возможности сдивгаемся назад @index-- @_p = n.p n.p.n = n.n # отвязываем if i < listLength - 1 # если i не последний n.n.p = n.p # выставляем следущему элементу указатель на пред. else @_b = @_p # иначе переустанавливаем оконечный указатель else if listLength > 1 # если бежать некуда, прорываемся p = @_p = @_f = n.n p.p = null else @index-- else # здусь мы ищем ближайшее расстояние до искомого индекса @index-- if start < i dp = Math.abs start - i ds = start de = listLength - start - 1 if dp <= ds and dp <= de # если ближе всех к поинтеру d = dp # расстояние (количество движений в сторону) n = @_p if start > i # нужно направление while d-- isnt 0 n = n.n else while d-- isnt 0 n = n.p else if ds <= dp and ds <= de # к началу d = start n = @_f while d-- isnt 0 n = n.n else # к концу d = listLength - start - 1 n = @_b while d-- isnt 0 n = n.p if dual dp = end - start + 1 @length -= dp res = new Array(dp) k = 1 res[0] = n e = n position = start while position++ < end e = e.n res[k++] = e if start <= i <= end # ?[start]..[i]..[end]? i = start - 1 if i < 0 # [start]..[i]..[end]? if end < listLength - 1 # [start]..[i]..[end].. i = start @_p = e.n else # [start]..[i]..[end] @_f = null else # ..[start]..[i]..[end]? @_p = n.p @index = i else if i > end @index -= dp - 1 else e = n if n.p? n.p.n = e.n else @_f = e.n if e.n? e.n.p = n.p else @_b = n.p if @_f is null @_f = @_b = @_p = new Node null n.n = n.p = null if returnNodes if dual i = res.length while i-- > 0 res[i].p = res[i].n = null return res return n @ # Очистка всего списка. Если список пуст уже, то вернётся `null` # @throw {TypeError} если переданы аргументы # @return {List|null} `this` или признак пустого списка clear: -> if arguments.length isnt 0 throw new TypeError "clear haven't arguments" len = @length if len is 0 return null p = @_f i = 0 while i++ < len n = p.n p.destructor() p = n @_p = @_b = @_f = new Node null @length = 0 @index = -1 @ # Удаляет все ссылки и очищает список, разрушая все узлы # @return {null} destructor: -> p = @_f len = @length i = 0 while i++ < len # Можно вызывать pop() но так на две строки больше, но яснее n = p.n p.destructor() p = n @_f = null @_b = null @length = 0 return null # Создаёт список на основе массива с указателем на первом элементе # @param {Array<*>} array массив сущностей # @throw {Error} если аргументов нет или больше одного # @throw {TypeError} если `array` не массив # @return {List} @fromArray = (array) -> l = arguments.length if l is 0 throw new Error "no arguments" if l isnt 1 throw new Error "too much arguments" if not (array instanceof Array) throw new TypeError "argument is not array" list = new List() len = array.length if len is 0 return list i = 1 list.length = len list._f.v = array[0] n = list._f while i < len n = n.n = new Node array[i], null, n i++ list._b = n list.index = 0 list module.exports = { List }