spldoublylinkedlist.inc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <?php
  2. /** @file spldoublylinkedlist.inc
  3. * @ingroup SPL
  4. * @brief class SplDoublyLinkedList
  5. * @author Etienne Kneuss
  6. * @date 2008 - 2009
  7. *
  8. * SPL - Standard PHP Library
  9. */
  10. /** @ingroup SPL
  11. * @brief Doubly Linked List
  12. * @since PHP 5.3
  13. *
  14. * The SplDoublyLinkedList class provides the main functionalities of a
  15. * doubly linked list (DLL).
  16. * @note The following userland implementation of Iterator is a bit different
  17. * from the internal one. Internally, iterators generated by nested
  18. * foreachs are independent, while they share the same traverse pointer
  19. * in userland.
  20. */
  21. class SplDoublyLinkedList implements Iterator, ArrayAccess, Countable
  22. {
  23. protected $_llist = array();
  24. protected $_it_mode = 0;
  25. protected $_it_pos = 0;
  26. /** Iterator mode
  27. * @see setIteratorMode
  28. */
  29. const IT_MODE_LIFO = 0x00000002;
  30. /** Iterator mode
  31. * @see setIteratorMode
  32. */
  33. const IT_MODE_FIFO = 0x00000000;
  34. /** Iterator mode
  35. * @see setIteratorMode
  36. */
  37. const IT_MODE_KEEP = 0x00000000;
  38. /** Iterator mode
  39. * @see setIteratorMode
  40. */
  41. const IT_MODE_DELETE = 0x00000001;
  42. /** @return the element popped from the end of the DLL.
  43. * @throw RuntimeException If the datastructure is empty.
  44. */
  45. public function pop()
  46. {
  47. if (count($this->_llist) == 0) {
  48. throw new RuntimeException("Can't pop from an empty datastructure");
  49. }
  50. return array_pop($this->_llist);
  51. }
  52. /** @return the element shifted from the beginning of the DLL.
  53. * @throw RuntimeException If the datastructure is empty.
  54. */
  55. public function shift()
  56. {
  57. if (count($this->_llist) == 0) {
  58. throw new RuntimeException("Can't shift from an empty datastructure");
  59. }
  60. return array_shift($this->_llist);
  61. }
  62. /** Pushes an element to the end of the DLL.
  63. * @param $data variable to add to the DLL.
  64. */
  65. public function push($data)
  66. {
  67. array_push($this->_llist, $data);
  68. return true;
  69. }
  70. /** Adds an element to the beginning of the DLL.
  71. * @param $data variable to add to the DLL.
  72. */
  73. public function unshift($data)
  74. {
  75. array_unshift($this->_llist, $data);
  76. return true;
  77. }
  78. /** @return the element at the beginning of the DLL.
  79. */
  80. public function top()
  81. {
  82. return end($this->_llist);
  83. }
  84. /** @return the element at the end of the DLL.
  85. */
  86. public function bottom()
  87. {
  88. return reset($this->_llist);
  89. }
  90. /** @return number elements in the DLL.
  91. */
  92. public function count()
  93. {
  94. return count($this->_llist);
  95. }
  96. /** @return whether the DLL is empty.
  97. */
  98. public function isEmpty()
  99. {
  100. return ($this->count() == 0);
  101. }
  102. /** Changes the iteration mode. There are two orthogonal sets of modes that
  103. * can be set:
  104. * - The direction of the iteration (either one or the other)
  105. * - SplDoublyLnkedList::IT_MODE_LIFO (Stack style)
  106. * - SplDoublyLnkedList::IT_MODE_FIFO (Queue style)
  107. *
  108. * - The behavior of the iterator (either one or the other)
  109. * - SplDoublyLnkedList::IT_MODE_DELETE (Elements are deleted by the iterator)
  110. * - SplDoublyLnkedList::IT_MODE_KEEP (Elements are traversed by the iterator)
  111. *
  112. * The default mode is 0 : SplDoublyLnkedList::IT_MODE_FIFO | SplDoublyLnkedList::IT_MODE_KEEP
  113. *
  114. * @param $mode new mode of iteration
  115. */
  116. public function setIteratorMode($mode)
  117. {
  118. $this->_it_mode = $mode;
  119. }
  120. /** @return the current iteration mode
  121. * @see setIteratorMode
  122. */
  123. public function getIteratorMode()
  124. {
  125. return $this->_it_mode;
  126. }
  127. /** Rewind to top iterator as set in constructor
  128. */
  129. public function rewind()
  130. {
  131. if ($this->_it_mode & self::IT_MODE_LIFO) {
  132. $this->_it_pos = count($this->_llist)-1;
  133. } else {
  134. $this->_it_pos = 0;
  135. }
  136. }
  137. /** @return whether iterator is valid
  138. */
  139. public function valid()
  140. {
  141. return array_key_exists($this->_it_pos, $this->_llist);
  142. }
  143. /** @return current key
  144. */
  145. public function key()
  146. {
  147. return $this->_it_pos;
  148. }
  149. /** @return current object
  150. */
  151. public function current()
  152. {
  153. return $this->_llist[$this->_it_pos];
  154. }
  155. /** Forward to next element
  156. */
  157. public function next()
  158. {
  159. if ($this->_it_mode & self::IT_MODE_LIFO) {
  160. if ($this->_it_mode & self::IT_MODE_DELETE) {
  161. $this->pop();
  162. }
  163. $this->_it_pos--;
  164. } else {
  165. if ($this->_it_mode & self::IT_MODE_DELETE) {
  166. $this->shift();
  167. } else {
  168. $this->_it_pos++;
  169. }
  170. }
  171. }
  172. /** @return whether a certain offset exists in the DLL
  173. *
  174. * @param $offset The offset
  175. * @throw OutOfRangeException If the offset is either invalid or out of
  176. * range.
  177. */
  178. public function offsetExists($offset)
  179. {
  180. if (!is_numeric($offset)) {
  181. throw new OutOfRangeException("Offset invalid or out of range");
  182. } else {
  183. return array_key_exists($offset, $this->_llist);
  184. }
  185. }
  186. /** @return the data at a certain offset in the DLL
  187. *
  188. * @param $offset The offset
  189. * @throw OutOfRangeException If the offset is either invalid or out of
  190. * range.
  191. */
  192. public function offsetGet($offset)
  193. {
  194. if ($this->_it_mode & self::IT_MODE_LIFO) {
  195. $realOffset = count($this->_llist)-$offset;
  196. } else {
  197. $realOffset = $offset;
  198. }
  199. if (!is_numeric($offset) || !array_key_exists($realOffset, $this->_llist)) {
  200. throw new OutOfRangeException("Offset invalid or out of range");
  201. } else {
  202. return $this->_llist[$realOffset];
  203. }
  204. }
  205. /** Defines the data at a certain offset in the DLL
  206. *
  207. * @param $offset The offset
  208. * @param $value New value
  209. * @throw OutOfRangeException If the offset is either invalid or out of
  210. * range.
  211. */
  212. public function offsetSet($offset, $value)
  213. {
  214. if ($offset === null) {
  215. return $this->push($value);
  216. }
  217. if ($this->_it_mode & self::IT_MODE_LIFO) {
  218. $realOffset = count($this->_llist)-$offset;
  219. } else {
  220. $realOffset = $offset;
  221. }
  222. if (!is_numeric($offset) || !array_key_exists($realOffset, $this->_llist)) {
  223. throw new OutOfRangeException("Offset invalid or out of range");
  224. } else {
  225. $this->_llist[$realOffset] = $value;
  226. }
  227. }
  228. /** Unsets the element at a certain offset in the DLL
  229. *
  230. * @param $offset The offset
  231. * @throw OutOfRangeException If the offset is either invalid or out of
  232. * range.
  233. */
  234. public function offsetUnset($offset)
  235. {
  236. if ($this->_it_mode & self::IT_MODE_LIFO) {
  237. $realOffset = count($this->_llist)-$offset;
  238. } else {
  239. $realOffset = $offset;
  240. }
  241. if (!is_numeric($offset) || !array_key_exists($realOffset, $this->_llist)) {
  242. throw new OutOfRangeException("Offset invalid or out of range");
  243. } else {
  244. array_splice($this->_llist, $realOffset, 1);
  245. }
  246. }
  247. }
  248. ?>