├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── LinkedList.html ├── LinkedList │ └── Node.html ├── css │ └── style.css ├── index.html ├── index.json ├── js │ └── doc.js └── search-index.js ├── shard.lock ├── shard.yml ├── spec ├── linked_list_spec.cr └── spec_helper.cr └── src ├── linked_list.cr └── linked_list_benchmark.cr /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 # use CircleCI 2.0 2 | jobs: # a collection of jobs 3 | build: 4 | docker: # run build steps with docker 5 | - image: crystallang/crystal:0.33.0 # primary docker container; all `steps` will run here. 6 | steps: # a collection of executable steps 7 | - checkout # checks out source code to working directory 8 | - restore_cache: # Restore dependency cache 9 | # Read about caching dependencies: https://circleci.com/docs/2.0/caching/ 10 | key: dependency-cache-{{ checksum "shard.lock" }} 11 | - run: 12 | name: Install dependencies. 13 | command: shards install 14 | - save_cache: # Step to save dependency cache 15 | key: dependency-cache-{{ checksum "shard.lock" }} 16 | paths: 17 | - ./lib 18 | - run: 19 | name: test 20 | command: crystal spec 21 | # See https://circleci.com/docs/2.0/deployment-integrations/ for deploy exampl -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | lib 3 | linked_list 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 abvdasker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Crystal Linked List [![CircleCI](https://circleci.com/gh/abvdasker/crystal-linked-list.svg?style=svg)](https://circleci.com/gh/abvdasker/crystal-linked-list) 2 | 3 | A simple linked list implementation in Crystal 4 | 5 | ## Installation 6 | 7 | Add this to a project's `shards.yml` 8 | 9 | ```yml 10 | dependencies: 11 | linked_list: 12 | git: https://github.com/abvdasker/crystal-linked-list.git 13 | ``` 14 | 15 | Then run `shards` 16 | 17 | ## Usage 18 | 19 | ```crystal 20 | require "linked_list" 21 | 22 | list = LinkedList(Int32 | String).new 23 | list.append(1) 24 | list.push(2) 25 | list << "foo" 26 | 27 | list.peek # "foo" 28 | list.pop # "foo" 29 | list.pop # 2 30 | list.unshift(1) 31 | list.shift # 1 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/LinkedList.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | LinkedList(A) - github.com/reduced-reptile/crystal-linked-list 18 | 19 | 20 | 21 | 58 | 59 | 60 |
61 |

62 | 63 | class LinkedList(A) 64 | 65 |

66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |

Overview

74 | 75 |

A linked list is a Enumerable (see the Enumerable module) data structure 76 | that stores multiple pieces of data in non contiguous locations in memory.

77 | 78 |

To create a linked list:

79 | 80 |
list = LinkedList(Int32).new
 81 | list.push(2)
 82 | puts list.pop
83 | 84 |

The above produces:

85 | 86 |
2
87 | 88 | 89 | 90 | 91 | 92 |

Included Modules

93 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |

Defined in:

109 | 110 | 111 | 112 | linked_list.cr 113 | 114 | 115 |
116 | 117 | 118 | 119 | 120 | 121 | 122 |

Constructors

123 | 147 | 148 | 149 | 150 | 151 | 152 |

Instance Method Summary

153 | 247 | 248 | 249 | 250 | 251 | 252 |
253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 |
285 | 286 | 287 |

Constructor Detail

288 | 289 |
290 |
291 | 292 | def self.new(values : Enumerable(A)) 293 | 294 | # 295 |
296 | 297 |
298 | 299 |

Creates a linked list with the values as the values of the nodes.

300 |
301 | 302 |
303 |
304 | 305 | [View source] 306 | 307 |
308 |
309 | 310 |
311 |
312 | 313 | def self.new 314 | 315 | # 316 |
317 | 318 |
319 | 320 |

Creates an empty linked list.

321 |
322 | 323 |
324 |
325 | 326 | [View source] 327 | 328 |
329 |
330 | 331 |
332 |
333 | 334 | def self.new(*values) 335 | 336 | # 337 |
338 | 339 |
340 | 341 |

Creates a linked list with the values as the values of the nodes.

342 |
343 | 344 |
345 |
346 | 347 | [View source] 348 | 349 |
350 |
351 | 352 | 353 | 354 | 355 | 356 | 357 |

Instance Method Detail

358 | 359 |
360 |
361 | 362 | def +(list : Enumerable(C)) forall C 363 | 364 | # 365 |
366 | 367 |
368 | 369 |

Returns a new LinkedList with all of the elements from the first list 370 | followed by all of the elements in the second list.

371 | 372 |
first_list = LinkedList(Int32).new(1, 2)
373 | second_list = LinkedList(String).new("foo", "bar")
374 | combined_list = first_list + second_list
375 | combined_list.peek() # => "bar"
376 | combined_list.shift() # => 1
377 |
378 | 379 |
380 |
381 | 382 | [View source] 383 | 384 |
385 |
386 | 387 |
388 |
389 | 390 | def <<(value : A) 391 | 392 | # 393 |
394 | 395 |
396 | 397 |

Override the << (shift) operator to add a value to the end of a 398 | linked list. This method returns itself so it can be chained.

399 | 400 |
list = LinkedList(Int32).new(1)
401 | list << 2 << 3
402 | list.pop() # => 3
403 | list.pop() # => 2
404 | list.pop() # => 1
405 |
406 | 407 |
408 |
409 | 410 | [View source] 411 | 412 |
413 |
414 | 415 |
416 |
417 | 418 | def append(value : A) 419 | 420 | # 421 |
422 | 423 |
424 | 425 |

Adds a value to the end of a linked list.

426 | 427 |
list = LinkedList(Int32).new
428 | list.append(1)
429 | list.push(2)
430 | list.pop() # => 2
431 | list.pop() # => 1
432 |
433 | 434 |
435 |
436 | 437 | [View source] 438 | 439 |
440 |
441 | 442 |
443 |
444 | 445 | def append(*values) 446 | 447 | # 448 |
449 | 450 |
451 | 452 |

Adds a list of values to the end of a linked list.

453 | 454 |
list = LinkedList(Int32 | String).new
455 | list.append(1, "foo")
456 | list.pop() # => "foo"
457 | list.pop() # => 1
458 |
459 | 460 |
461 |
462 | 463 | [View source] 464 | 465 |
466 |
467 | 468 |
469 |
470 | 471 | def concat(list : LinkedList(A)) 472 | 473 | # 474 |
475 | 476 |
477 | 478 |

Adds all the elemenets of the list to the end of the current linked list.

479 | 480 |
first_list = LinkedList(Int32).new(1, 2)
481 | second_list = LinkedList(Int32).new(3, 4)
482 | combined_list = first_list.concat(second_list)
483 | combined_list.peek() # => 4
484 | combined_list.shift() # => 1
485 |
486 | 487 |
488 |
489 | 490 | [View source] 491 | 492 |
493 |
494 | 495 |
496 |
497 | 498 | def each(&block) 499 | 500 | # 501 |
502 | 503 |
504 | 505 |

Iterates over all the values in the linked list.

506 | 507 |
values = [1, 2, 3]
508 | list = LinkedList(Int32).new(values)
509 | list.each do |elem|
510 |   puts elem
511 | end
512 | 
513 | The above produces:
514 | 
515 | 516 |

1 517 | 2 518 | 3

519 | 520 |
521 |
522 | 523 |
524 |
525 | 526 | [View source] 527 | 528 |
529 |
530 | 531 |
532 |
533 | 534 | def empty? 535 | 536 | # 537 |
538 | 539 |
540 | 541 |

Returns true if and only if there are no elements in the list.

542 | 543 |
list = LinkedList(Int32).new
544 | list.empty? # => true
545 | list.push(1)
546 | list.empty? # => false
547 |
548 | 549 |
550 |
551 | 552 | [View source] 553 | 554 |
555 |
556 | 557 |
558 |
559 | 560 | def peek 561 | 562 | # 563 |
564 | 565 |
566 | 567 |

Returns the value of the tail of the linked list, 568 | or nil if no value was supplied.

569 | 570 |
list = LinkedList(Float64).new(1.23)
571 | list.push(4.56)
572 | list.peek() # => 4.56
573 |
574 | 575 |
576 |
577 | 578 | [View source] 579 | 580 |
581 |
582 | 583 |
584 |
585 | 586 | def pop 587 | 588 | # 589 |
590 | 591 |
592 | 593 |

Returns the last Node from the list and removes it.

594 | 595 |
list = LinkedList(Float64).new(1.23)
596 | list.push(4.56)
597 | list.pop() # => 4.56
598 | list.peek() # => 1.23
599 |
600 | 601 |
602 |
603 | 604 | [View source] 605 | 606 |
607 |
608 | 609 |
610 |
611 | 612 | def push(value : A) 613 | 614 | # 615 |
616 | 617 |
618 | 619 |

Adds a value to the end of a linked list.

620 | 621 |
list = LinkedList(Int32).new
622 | list.append(1)
623 | list.push(2)
624 | list.pop() # => 2
625 | list.pop() # => 1
626 |
627 | 628 |
629 |
630 | 631 | [View source] 632 | 633 |
634 |
635 | 636 |
637 |
638 | 639 | def reverse 640 | 641 | # 642 |
643 | 644 |
645 | 646 |

Creates a copy of the LinkedList with the order reversed.

647 | 648 |
list = LinkedList(Int32).new(1, 2, 3)
649 | reversed_list = list.reverse
650 | list.pop() # => 1
651 | list.pop() # => 2
652 | list.pop() # => 3
653 |
654 | 655 |
656 |
657 | 658 | [View source] 659 | 660 |
661 |
662 | 663 |
664 |
665 | 666 | def shift 667 | 668 | # 669 |
670 | 671 |
672 | 673 |

Returns the first Node from the list and removes it.

674 | 675 |
list = LinkedList(Float64).new(1.23)
676 | list.push(4.56)
677 | list.shift() # => 1.23
678 | list.peek() # => 4.56
679 |
680 | 681 |
682 |
683 | 684 | [View source] 685 | 686 |
687 |
688 | 689 |
690 |
691 | 692 | def unshift(value : A) 693 | 694 | # 695 |
696 | 697 |
698 | 699 |

Adds a value to the beginning of a linked list.

700 | 701 |
list = LinkedList(Int32).new(1)
702 | list.unshift(2)
703 | list.pop() # => 1
704 | list.pop() # => 2
705 |
706 | 707 |
708 |
709 | 710 | [View source] 711 | 712 |
713 |
714 | 715 | 716 | 717 | 718 | 719 |
720 | 721 | 722 | 723 | -------------------------------------------------------------------------------- /docs/LinkedList/Node.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | LinkedList::Node(T) - github.com/reduced-reptile/crystal-linked-list 18 | 19 | 20 | 21 | 58 | 59 | 60 |
61 |

62 | 63 | class LinkedList::Node(T) 64 | 65 |

66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |

Overview

74 | 75 |

A node is the building block of linked lists consisting of a values 76 | and a pointer to the next node in the linked list.

77 | 78 |

To create a node:

79 | 80 |
node = Node.new(5)
 81 | puts node.value
82 | 83 |

The above produces:

84 | 85 |
5
86 | 87 |

Check the value of the node with #value. 88 | Get the next node in the list with #next.

89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 |

Defined in:

104 | 105 | 106 | 107 | linked_list.cr 108 | 109 | 110 |
111 | 112 | 113 | 114 | 115 | 116 | 117 |

Constructors

118 | 135 | 136 | 137 | 138 | 139 | 140 |

Instance Method Summary

141 | 165 | 166 | 167 | 168 | 169 | 170 |
171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 |
193 | 194 | 195 |

Constructor Detail

196 | 197 |
198 |
199 | 200 | def self.new(value : T) 201 | 202 | # 203 |
204 | 205 |
206 | 207 |

Creates a node with the specified value.

208 |
209 | 210 |
211 |
212 | 213 | [View source] 214 | 215 |
216 |
217 | 218 |
219 |
220 | 221 | def self.new 222 | 223 | # 224 |
225 | 226 |
227 | 228 |

Creates a node with no value.

229 |
230 | 231 |
232 |
233 | 234 | [View source] 235 | 236 |
237 |
238 | 239 | 240 | 241 | 242 | 243 | 244 |

Instance Method Detail

245 | 246 |
247 |
248 | 249 | def next 250 | 251 | # 252 |
253 | 254 |
255 | 256 |

Returns the next node in the linked list, or nil if it is the tail.

257 | 258 |
node = Node.new(1)
259 | node.next = Node.new(2)
260 | node.next.value # => 2
261 |
262 | 263 |
264 |
265 | 266 | [View source] 267 | 268 |
269 |
270 | 271 |
272 |
273 | 274 | def next=(next_node : LinkedList::Node(T)?) 275 | 276 | # 277 |
278 | 279 |
280 | 281 |

Sets the next node in the linked list to next_node

282 | 283 |
node = Node.new(1)
284 | node.next = Node.new(2)
285 | node.next.value # => 2
286 |
287 | 288 |
289 |
290 | 291 | [View source] 292 | 293 |
294 |
295 | 296 |
297 |
298 | 299 | def value 300 | 301 | # 302 |
303 | 304 |
305 | 306 |

Returns the value of the node, or nil if no value was supplied

307 | 308 |
Node.new(1).value # => 1
309 |
310 | 311 |
312 |
313 | 314 | [View source] 315 | 316 |
317 |
318 | 319 | 320 | 321 | 322 | 323 |
324 | 325 | 326 | 327 | -------------------------------------------------------------------------------- /docs/css/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | background: #FFFFFF; 3 | position: relative; 4 | margin: 0; 5 | padding: 0; 6 | width: 100%; 7 | height: 100%; 8 | overflow: hidden; 9 | } 10 | 11 | body { 12 | font-family: "Avenir", "Tahoma", "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif; 13 | color: #333; 14 | line-height: 1.5; 15 | } 16 | 17 | a { 18 | color: #263F6C; 19 | } 20 | 21 | a:visited { 22 | color: #112750; 23 | } 24 | 25 | h1, h2, h3, h4, h5, h6 { 26 | margin: 35px 0 25px; 27 | color: #444444; 28 | } 29 | 30 | h1.type-name { 31 | color: #47266E; 32 | margin: 20px 0 30px; 33 | background-color: #F8F8F8; 34 | padding: 10px 12px; 35 | border: 1px solid #EBEBEB; 36 | border-radius: 2px; 37 | } 38 | 39 | h2 { 40 | border-bottom: 1px solid #E6E6E6; 41 | padding-bottom: 5px; 42 | } 43 | 44 | body { 45 | display: flex; 46 | } 47 | 48 | .sidebar, .main-content { 49 | overflow: auto; 50 | } 51 | 52 | .sidebar { 53 | width: 30em; 54 | color: #F8F4FD; 55 | background-color: #2E1052; 56 | padding: 0 0 30px; 57 | box-shadow: inset -3px 0 4px rgba(0,0,0,.35); 58 | line-height: 1.2; 59 | } 60 | 61 | .sidebar .search-box { 62 | padding: 8px 9px; 63 | } 64 | 65 | .sidebar input { 66 | display: block; 67 | box-sizing: border-box; 68 | margin: 0; 69 | padding: 5px; 70 | font: inherit; 71 | font-family: inherit; 72 | line-height: 1.2; 73 | width: 100%; 74 | border: 0; 75 | outline: 0; 76 | border-radius: 2px; 77 | box-shadow: 0px 3px 5px rgba(0,0,0,.25); 78 | transition: box-shadow .12s; 79 | } 80 | 81 | .sidebar input:focus { 82 | box-shadow: 0px 5px 6px rgba(0,0,0,.5); 83 | } 84 | 85 | .sidebar input::-webkit-input-placeholder { /* Chrome/Opera/Safari */ 86 | color: #C8C8C8; 87 | font-size: 14px; 88 | text-indent: 2px; 89 | } 90 | 91 | .sidebar input::-moz-placeholder { /* Firefox 19+ */ 92 | color: #C8C8C8; 93 | font-size: 14px; 94 | text-indent: 2px; 95 | } 96 | 97 | .sidebar input:-ms-input-placeholder { /* IE 10+ */ 98 | color: #C8C8C8; 99 | font-size: 14px; 100 | text-indent: 2px; 101 | } 102 | 103 | .sidebar input:-moz-placeholder { /* Firefox 18- */ 104 | color: #C8C8C8; 105 | font-size: 14px; 106 | text-indent: 2px; 107 | } 108 | 109 | .sidebar ul { 110 | margin: 0; 111 | padding: 0; 112 | list-style: none outside; 113 | } 114 | 115 | .sidebar li { 116 | display: block; 117 | position: relative; 118 | } 119 | 120 | .types-list li.hide { 121 | display: none; 122 | } 123 | 124 | .sidebar a { 125 | text-decoration: none; 126 | color: inherit; 127 | transition: color .14s; 128 | } 129 | .types-list a { 130 | display: block; 131 | padding: 5px 15px 5px 30px; 132 | } 133 | 134 | .types-list { 135 | display: block; 136 | } 137 | 138 | .sidebar a:focus { 139 | outline: 1px solid #D1B7F1; 140 | } 141 | 142 | .types-list a { 143 | padding: 5px 15px 5px 30px; 144 | } 145 | 146 | .sidebar .current > a, 147 | .sidebar a:hover { 148 | color: #866BA6; 149 | } 150 | 151 | .repository-links { 152 | padding: 5px 15px 5px 30px; 153 | } 154 | 155 | .types-list li ul { 156 | overflow: hidden; 157 | height: 0; 158 | max-height: 0; 159 | transition: 1s ease-in-out; 160 | } 161 | 162 | .types-list li.parent { 163 | padding-left: 30px; 164 | } 165 | 166 | .types-list li.parent::before { 167 | box-sizing: border-box; 168 | content: "▼"; 169 | display: block; 170 | width: 30px; 171 | height: 30px; 172 | position: absolute; 173 | top: 0; 174 | left: 0; 175 | text-align: center; 176 | color: white; 177 | font-size: 8px; 178 | line-height: 30px; 179 | transform: rotateZ(-90deg); 180 | cursor: pointer; 181 | transition: .2s linear; 182 | } 183 | 184 | 185 | .types-list li.parent > a { 186 | padding-left: 0; 187 | } 188 | 189 | .types-list li.parent.open::before { 190 | transform: rotateZ(0); 191 | } 192 | 193 | .types-list li.open > ul { 194 | height: auto; 195 | max-height: 1000em; 196 | } 197 | 198 | .main-content { 199 | padding: 0 30px 30px 30px; 200 | width: 100%; 201 | } 202 | 203 | .kind { 204 | font-size: 60%; 205 | color: #866BA6; 206 | } 207 | 208 | .superclass-hierarchy { 209 | margin: -15px 0 30px 0; 210 | padding: 0; 211 | list-style: none outside; 212 | font-size: 80%; 213 | } 214 | 215 | .superclass-hierarchy .superclass { 216 | display: inline-block; 217 | margin: 0 7px 0 0; 218 | padding: 0; 219 | } 220 | 221 | .superclass-hierarchy .superclass + .superclass::before { 222 | content: "<"; 223 | margin-right: 7px; 224 | } 225 | 226 | .other-types-list li { 227 | display: inline-block; 228 | } 229 | 230 | .other-types-list, 231 | .list-summary { 232 | margin: 0 0 30px 0; 233 | padding: 0; 234 | list-style: none outside; 235 | } 236 | 237 | .entry-const { 238 | font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace; 239 | } 240 | 241 | .entry-const code { 242 | white-space: pre-wrap; 243 | } 244 | 245 | .entry-summary { 246 | padding-bottom: 4px; 247 | } 248 | 249 | .superclass-hierarchy .superclass a, 250 | .other-type a, 251 | .entry-summary .signature { 252 | padding: 4px 8px; 253 | margin-bottom: 4px; 254 | display: inline-block; 255 | background-color: #f8f8f8; 256 | color: #47266E; 257 | border: 1px solid #f0f0f0; 258 | text-decoration: none; 259 | border-radius: 3px; 260 | font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace; 261 | transition: background .15s, border-color .15s; 262 | } 263 | 264 | .superclass-hierarchy .superclass a:hover, 265 | .other-type a:hover, 266 | .entry-summary .signature:hover { 267 | background: #D5CAE3; 268 | border-color: #624288; 269 | } 270 | 271 | .entry-summary .summary { 272 | padding-left: 32px; 273 | } 274 | 275 | .entry-summary .summary p { 276 | margin: 12px 0 16px; 277 | } 278 | 279 | .entry-summary a { 280 | text-decoration: none; 281 | } 282 | 283 | .entry-detail { 284 | padding: 30px 0; 285 | } 286 | 287 | .entry-detail .signature { 288 | position: relative; 289 | padding: 5px 15px; 290 | margin-bottom: 10px; 291 | display: block; 292 | border-radius: 5px; 293 | background-color: #f8f8f8; 294 | color: #47266E; 295 | border: 1px solid #f0f0f0; 296 | font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace; 297 | transition: .2s ease-in-out; 298 | } 299 | 300 | .entry-detail:target .signature { 301 | background-color: #D5CAE3; 302 | border: 1px solid #624288; 303 | } 304 | 305 | .entry-detail .signature .method-permalink { 306 | position: absolute; 307 | top: 0; 308 | left: -35px; 309 | padding: 5px 15px; 310 | text-decoration: none; 311 | font-weight: bold; 312 | color: #624288; 313 | opacity: .4; 314 | transition: opacity .2s; 315 | } 316 | 317 | .entry-detail .signature .method-permalink:hover { 318 | opacity: 1; 319 | } 320 | 321 | .entry-detail:target .signature .method-permalink { 322 | opacity: 1; 323 | } 324 | 325 | .methods-inherited { 326 | padding-right: 10%; 327 | line-height: 1.5em; 328 | } 329 | 330 | .methods-inherited h3 { 331 | margin-bottom: 4px; 332 | } 333 | 334 | .methods-inherited a { 335 | display: inline-block; 336 | text-decoration: none; 337 | color: #47266E; 338 | } 339 | 340 | .methods-inherited a:hover { 341 | text-decoration: underline; 342 | color: #6C518B; 343 | } 344 | 345 | .methods-inherited .tooltip>span { 346 | background: #D5CAE3; 347 | padding: 4px 8px; 348 | border-radius: 3px; 349 | margin: -4px -8px; 350 | } 351 | 352 | .methods-inherited .tooltip * { 353 | color: #47266E; 354 | } 355 | 356 | pre { 357 | padding: 10px 20px; 358 | margin-top: 4px; 359 | border-radius: 3px; 360 | line-height: 1.45; 361 | overflow: auto; 362 | color: #333; 363 | background: #fdfdfd; 364 | font-size: 14px; 365 | border: 1px solid #eee; 366 | } 367 | 368 | code { 369 | font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace; 370 | } 371 | 372 | :not(pre) > code { 373 | background-color: rgba(40,35,30,0.05); 374 | padding: 0.2em 0.4em; 375 | font-size: 85%; 376 | border-radius: 3px; 377 | } 378 | 379 | span.flag { 380 | padding: 2px 4px 1px; 381 | border-radius: 3px; 382 | margin-right: 3px; 383 | font-size: 11px; 384 | border: 1px solid transparent; 385 | } 386 | 387 | span.flag.orange { 388 | background-color: #EE8737; 389 | color: #FCEBDD; 390 | border-color: #EB7317; 391 | } 392 | 393 | span.flag.yellow { 394 | background-color: #E4B91C; 395 | color: #FCF8E8; 396 | border-color: #B69115; 397 | } 398 | 399 | span.flag.green { 400 | background-color: #469C14; 401 | color: #E2F9D3; 402 | border-color: #34700E; 403 | } 404 | 405 | span.flag.red { 406 | background-color: #BF1919; 407 | color: #F9ECEC; 408 | border-color: #822C2C; 409 | } 410 | 411 | span.flag.purple { 412 | background-color: #2E1052; 413 | color: #ECE1F9; 414 | border-color: #1F0B37; 415 | } 416 | 417 | .tooltip>span { 418 | position: absolute; 419 | opacity: 0; 420 | display: none; 421 | pointer-events: none; 422 | } 423 | 424 | .tooltip:hover>span { 425 | display: inline-block; 426 | opacity: 1; 427 | } 428 | 429 | .c { 430 | color: #969896; 431 | } 432 | 433 | .n { 434 | color: #0086b3; 435 | } 436 | 437 | .t { 438 | color: #0086b3; 439 | } 440 | 441 | .s { 442 | color: #183691; 443 | } 444 | 445 | .i { 446 | color: #7f5030; 447 | } 448 | 449 | .k { 450 | color: #a71d5d; 451 | } 452 | 453 | .o { 454 | color: #a71d5d; 455 | } 456 | 457 | .m { 458 | color: #795da3; 459 | } 460 | 461 | .hidden { 462 | display: none; 463 | } 464 | .search-results { 465 | font-size: 90%; 466 | line-height: 1.3; 467 | } 468 | 469 | .search-results mark { 470 | color: inherit; 471 | background: transparent; 472 | font-weight: bold; 473 | } 474 | .search-result { 475 | padding: 5px 8px 5px 5px; 476 | cursor: pointer; 477 | border-left: 5px solid transparent; 478 | transform: translateX(-3px); 479 | transition: all .2s, background-color 0s, border .02s; 480 | min-height: 3.2em; 481 | } 482 | .search-result.current { 483 | border-left-color: #ddd; 484 | background-color: rgba(200,200,200,0.4); 485 | transform: translateX(0); 486 | transition: all .2s, background-color .5s, border 0s; 487 | } 488 | .search-result.current:hover, 489 | .search-result.current:focus { 490 | border-left-color: #866BA6; 491 | } 492 | .search-result:not(.current):nth-child(2n) { 493 | background-color: rgba(255,255,255,.06); 494 | } 495 | .search-result__title { 496 | font-size: 105%; 497 | word-break: break-all; 498 | line-height: 1.1; 499 | padding: 3px 0; 500 | } 501 | .search-result__title strong { 502 | font-weight: normal; 503 | } 504 | .search-results .search-result__title > a { 505 | padding: 0; 506 | display: block; 507 | } 508 | .search-result__title > a > .args { 509 | color: #dddddd; 510 | font-weight: 300; 511 | transition: inherit; 512 | font-size: 88%; 513 | line-height: 1.2; 514 | letter-spacing: -.02em; 515 | } 516 | .search-result__title > a > .args * { 517 | color: inherit; 518 | } 519 | 520 | .search-result a, 521 | .search-result a:hover { 522 | color: inherit; 523 | } 524 | .search-result:not(.current):hover .search-result__title > a, 525 | .search-result:not(.current):focus .search-result__title > a, 526 | .search-result__title > a:focus { 527 | color: #866BA6; 528 | } 529 | .search-result:not(.current):hover .args, 530 | .search-result:not(.current):focus .args { 531 | color: #6a5a7d; 532 | } 533 | 534 | .search-result__type { 535 | color: #e8e8e8; 536 | font-weight: 300; 537 | } 538 | .search-result__doc { 539 | color: #bbbbbb; 540 | font-size: 90%; 541 | } 542 | .search-result__doc p { 543 | margin: 0; 544 | text-overflow: ellipsis; 545 | display: -webkit-box; 546 | -webkit-box-orient: vertical; 547 | -webkit-line-clamp: 2; 548 | overflow: hidden; 549 | line-height: 1.2em; 550 | max-height: 2.4em; 551 | } 552 | 553 | .js-modal-visible .modal-background { 554 | display: flex; 555 | } 556 | .main-content { 557 | position: relative; 558 | } 559 | .modal-background { 560 | position: absolute; 561 | display: none; 562 | height: 100%; 563 | width: 100%; 564 | background: rgba(120,120,120,.4); 565 | z-index: 100; 566 | align-items: center; 567 | justify-content: center; 568 | } 569 | .usage-modal { 570 | max-width: 90%; 571 | background: #fff; 572 | border: 2px solid #ccc; 573 | border-radius: 9px; 574 | padding: 5px 15px 20px; 575 | min-width: 50%; 576 | color: #555; 577 | position: relative; 578 | transform: scale(.5); 579 | transition: transform 200ms; 580 | } 581 | .js-modal-visible .usage-modal { 582 | transform: scale(1); 583 | } 584 | .usage-modal > .close-button { 585 | position: absolute; 586 | right: 15px; 587 | top: 8px; 588 | color: #aaa; 589 | font-size: 27px; 590 | cursor: pointer; 591 | } 592 | .usage-modal > .close-button:hover { 593 | text-shadow: 2px 2px 2px #ccc; 594 | color: #999; 595 | } 596 | .modal-title { 597 | margin: 0; 598 | text-align: center; 599 | font-weight: normal; 600 | color: #666; 601 | border-bottom: 2px solid #ddd; 602 | padding: 10px; 603 | } 604 | .usage-list { 605 | padding: 0; 606 | margin: 13px; 607 | } 608 | .usage-list > li { 609 | padding: 5px 2px; 610 | overflow: auto; 611 | padding-left: 100px; 612 | min-width: 12em; 613 | } 614 | .usage-modal kbd { 615 | background: #eee; 616 | border: 1px solid #ccc; 617 | border-bottom-width: 2px; 618 | border-radius: 3px; 619 | padding: 3px 8px; 620 | font-family: monospace; 621 | margin-right: 2px; 622 | display: inline-block; 623 | } 624 | .usage-key { 625 | float: left; 626 | clear: left; 627 | margin-left: -100px; 628 | margin-right: 12px; 629 | } 630 | .doc-inherited { 631 | font-weight: bold; 632 | } 633 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | README - github.com/reduced-reptile/crystal-linked-list 18 | 19 | 20 | 21 | 58 | 59 | 60 |
61 |

Crystal Linked List CircleCI

62 | 63 |

A simple linked list implementation in Crystal

64 | 65 |

Installation

66 | 67 |

Add this to a project's shards.yml

68 | 69 |
dependencies:
70 |   linked_list:
71 |     git: https://github.com/abvdasker/linked_list.git
72 | 73 |

Then run crystal deps

74 | 75 |

Usage

76 | 77 |
require "linked_list"
78 | 
79 | list = LinkedList(Int32 | String).new
80 | list.append(1)
81 | list.push(2)
82 | list << "foo"
83 | 
84 | list.peek        # "foo"
85 | list.pop         # "foo"
86 | list.pop         # 2
87 | list.unshift(1)
88 | list.shift       # 1
89 |
90 | 91 | 92 | -------------------------------------------------------------------------------- /docs/index.json: -------------------------------------------------------------------------------- 1 | {"repository_name":"github.com/reduced-reptile/crystal-linked-list","body":"# Crystal Linked List [![CircleCI](https://circleci.com/gh/abvdasker/crystal-linked-list.svg?style=svg)](https://circleci.com/gh/abvdasker/crystal-linked-list)\n\nA simple linked list implementation in Crystal\n\n## Installation\n\nAdd this to a project's `shards.yml`\n\n```yml\ndependencies:\n linked_list:\n git: https://github.com/abvdasker/linked_list.git\n```\n\nThen run `crystal deps`\n\n## Usage\n\n```crystal\nrequire \"linked_list\"\n\nlist = LinkedList(Int32 | String).new\nlist.append(1)\nlist.push(2)\nlist << \"foo\"\n\nlist.peek # \"foo\"\nlist.pop # \"foo\"\nlist.pop # 2\nlist.unshift(1)\nlist.shift # 1\n```\n","program":{"html_id":"github.com/reduced-reptile/crystal-linked-list/toplevel","path":"toplevel.html","kind":"module","full_name":"Top Level Namespace","name":"Top Level Namespace","abstract":false,"superclass":null,"ancestors":[],"locations":[],"repository_name":"github.com/reduced-reptile/crystal-linked-list","program":true,"enum":false,"alias":false,"aliased":"","const":false,"constants":[],"included_modules":[],"extended_modules":[],"subclasses":[],"including_types":[],"namespace":null,"doc":null,"summary":null,"class_methods":[],"constructors":[],"instance_methods":[],"macros":[],"types":[{"html_id":"github.com/reduced-reptile/crystal-linked-list/LinkedList","path":"LinkedList.html","kind":"class","full_name":"LinkedList(A)","name":"LinkedList","abstract":false,"superclass":{"html_id":"github.com/reduced-reptile/crystal-linked-list/Reference","kind":"class","full_name":"Reference","name":"Reference"},"ancestors":[{"html_id":"github.com/reduced-reptile/crystal-linked-list/Enumerable","kind":"module","full_name":"Enumerable","name":"Enumerable"},{"html_id":"github.com/reduced-reptile/crystal-linked-list/Reference","kind":"class","full_name":"Reference","name":"Reference"},{"html_id":"github.com/reduced-reptile/crystal-linked-list/Object","kind":"class","full_name":"Object","name":"Object"}],"locations":[{"filename":"linked_list.cr","line_number":17,"url":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr"}],"repository_name":"github.com/reduced-reptile/crystal-linked-list","program":false,"enum":false,"alias":false,"aliased":"","const":false,"constants":[],"included_modules":[{"html_id":"github.com/reduced-reptile/crystal-linked-list/Enumerable","kind":"module","full_name":"Enumerable","name":"Enumerable"}],"extended_modules":[],"subclasses":[],"including_types":[],"namespace":null,"doc":"A linked list is a **Enumerable** (see the `Enumerable` module) data structure\nthat stores multiple pieces of data in non contiguous locations in memory.\n\nTo create a linked list:\n\n```\nlist = LinkedList(Int32).new\nlist.push(2)\nputs list.pop\n```\n\nThe above produces:\n\n```text\n2\n```","summary":"

A linked list is a Enumerable (see the Enumerable module) data structure that stores multiple pieces of data in non contiguous locations in memory.

","class_methods":[],"constructors":[{"id":"new(values:Enumerable(A))-class-method","html_id":"new(values:Enumerable(A))-class-method","name":"new","doc":"Creates a linked list with the *values* as the values of the nodes.","summary":"

Creates a linked list with the values as the values of the nodes.

","abstract":false,"args":[{"name":"values","doc":null,"default_value":"","external_name":"values","restriction":"Enumerable(A)"}],"args_string":"(values : Enumerable(A))","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L32","def":{"name":"new","args":[{"name":"values","doc":null,"default_value":"","external_name":"values","restriction":"Enumerable(A)"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"_ = LinkedList(A).allocate\n_.initialize(values)\nif _.responds_to?(:finalize)\n ::GC.add_finalizer(_)\nend\n_\n"}},{"id":"new-class-method","html_id":"new-class-method","name":"new","doc":"Creates an empty linked list.","summary":"

Creates an empty linked list.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L21","def":{"name":"new","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"_ = LinkedList(A).allocate\n_.initialize\nif _.responds_to?(:finalize)\n ::GC.add_finalizer(_)\nend\n_\n"}},{"id":"new(*values)-class-method","html_id":"new(*values)-class-method","name":"new","doc":"Creates a linked list with the *values* as the values of the nodes.","summary":"

Creates a linked list with the values as the values of the nodes.

","abstract":false,"args":[{"name":"values","doc":null,"default_value":"","external_name":"values","restriction":""}],"args_string":"(*values)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L27","def":{"name":"new","args":[{"name":"values","doc":null,"default_value":"","external_name":"values","restriction":""}],"double_splat":null,"splat_index":0,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"_ = LinkedList(A).allocate\n_.initialize(*values)\nif _.responds_to?(:finalize)\n ::GC.add_finalizer(_)\nend\n_\n"}}],"instance_methods":[{"id":"+(list:Enumerable(C))forallC-instance-method","html_id":"+(list:Enumerable(C))forallC-instance-method","name":"+","doc":"Returns a new `LinkedList` with all of the elements from the first list\nfollowed by all of the elements in the second *list*.\n\n```\nfirst_list = LinkedList(Int32).new(1, 2)\nsecond_list = LinkedList(String).new(\"foo\", \"bar\")\ncombined_list = first_list + second_list\ncombined_list.peek() # => \"bar\"\ncombined_list.shift() # => 1\n```","summary":"

Returns a new LinkedList with all of the elements from the first list followed by all of the elements in the second list.

","abstract":false,"args":[{"name":"list","doc":null,"default_value":"","external_name":"list","restriction":"Enumerable(C)"}],"args_string":"(list : Enumerable(C)) forall C","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L191","def":{"name":"+","args":[{"name":"list","doc":null,"default_value":"","external_name":"list","restriction":"Enumerable(C)"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"LinkedList(A | C).new.tap do |new_list|\n each do |value|\n new_list.append(value)\n end\n list.each do |value|\n new_list.append(value)\n end\nend"}},{"id":"<<(value:A)-instance-method","html_id":"<<(value:A)-instance-method","name":"<<","doc":"Override the << (shift) operator to add a *value* to the end of a\nlinked list. This method returns itself so it can be chained.\n\n```\nlist = LinkedList(Int32).new(1)\nlist << 2 << 3\nlist.pop() # => 3\nlist.pop() # => 2\nlist.pop() # => 1\n```","summary":"

Override the << (shift) operator to add a value to the end of a linked list.

","abstract":false,"args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"args_string":"(value : A)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L71","def":{"name":"<<","args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"append(value)\nself\n"}},{"id":"append(value:A)-instance-method","html_id":"append(value:A)-instance-method","name":"append","doc":"Adds a *value* to the end of a linked list.\n\n```\nlist = LinkedList(Int32).new\nlist.append(1)\nlist.push(2)\nlist.pop() # => 2\nlist.pop() # => 1\n```","summary":"

Adds a value to the end of a linked list.

","abstract":false,"args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"args_string":"(value : A)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L49","def":{"name":"append","args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"new_node = Node(A).new(value)\n@tail.next = new_node\n@tail = new_node\nnew_node.value\n"}},{"id":"append(*values)-instance-method","html_id":"append(*values)-instance-method","name":"append","doc":"Adds a list of *values* to the end of a linked list.\n\n```\nlist = LinkedList(Int32 | String).new\nlist.append(1, \"foo\")\nlist.pop() # => \"foo\"\nlist.pop() # => 1\n```","summary":"

Adds a list of values to the end of a linked list.

","abstract":false,"args":[{"name":"values","doc":null,"default_value":"","external_name":"values","restriction":""}],"args_string":"(*values)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L84","def":{"name":"append","args":[{"name":"values","doc":null,"default_value":"","external_name":"values","restriction":""}],"double_splat":null,"splat_index":0,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"values.each do |value|\n append(value)\nend"}},{"id":"concat(list:LinkedList(A))-instance-method","html_id":"concat(list:LinkedList(A))-instance-method","name":"concat","doc":"Adds all the elemenets of the *list* to the end of the current linked list.\n\n```\nfirst_list = LinkedList(Int32).new(1, 2)\nsecond_list = LinkedList(Int32).new(3, 4)\ncombined_list = first_list.concat(second_list)\ncombined_list.peek() # => 4\ncombined_list.shift() # => 1\n```","summary":"

Adds all the elemenets of the list to the end of the current linked list.

","abstract":false,"args":[{"name":"list","doc":null,"default_value":"","external_name":"list","restriction":"LinkedList(A)"}],"args_string":"(list : LinkedList(A))","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L211","def":{"name":"concat","args":[{"name":"list","doc":null,"default_value":"","external_name":"list","restriction":"LinkedList(A)"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"@tail.next = list.head.next\n@tail = list.tail\nself\n"}},{"id":"each(&block)-instance-method","html_id":"each(&block)-instance-method","name":"each","doc":"Iterates over all the values in the linked list.\n\n```\nvalues = [1, 2, 3]\nlist = LinkedList(Int32).new(values)\nlist.each do |elem|\n puts elem\nend\n\nThe above produces:\n\n```text\n1\n2\n3\n```","summary":"

Iterates over all the values in the linked list.

","abstract":false,"args":[],"args_string":"(&block)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L174","def":{"name":"each","args":[],"double_splat":null,"splat_index":null,"yields":1,"block_arg":null,"return_type":"","visibility":"Public","body":"each_node do |node|\n yield node.value\nend\nself\n"}},{"id":"empty?-instance-method","html_id":"empty?-instance-method","name":"empty?","doc":"Returns true if and only if there are no elements in the list.\n\n```\nlist = LinkedList(Int32).new\nlist.empty? # => true\nlist.push(1)\nlist.empty? # => false\n```","summary":"

Returns true if and only if there are no elements in the list.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L225","def":{"name":"empty?","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"@head == @tail"}},{"id":"peek-instance-method","html_id":"peek-instance-method","name":"peek","doc":"Returns the value of the tail of the linked list,\nor nil if no value was supplied.\n\n```\nlist = LinkedList(Float64).new(1.23)\nlist.push(4.56)\nlist.peek() # => 4.56\n```","summary":"

Returns the value of the tail of the linked list, or nil if no value was supplied.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L132","def":{"name":"peek","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"@tail.value"}},{"id":"pop-instance-method","html_id":"pop-instance-method","name":"pop","doc":"Returns the last `Node` from the list and removes it.\n\n```\nlist = LinkedList(Float64).new(1.23)\nlist.push(4.56)\nlist.pop() # => 4.56\nlist.peek() # => 1.23\n```","summary":"

Returns the last Node from the list and removes it.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L144","def":{"name":"pop","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"if @head == @tail\n return nil\nend\nlast = @tail\ncurrent = @head\nwhile current.next != last\n current = current.next.not_nil!\nend\ncurrent.next = nil\n@tail = current\nlast.value\n"}},{"id":"push(value:A)-instance-method","html_id":"push(value:A)-instance-method","name":"push","doc":"Adds a *value* to the end of a linked list.\n\n```\nlist = LinkedList(Int32).new\nlist.append(1)\nlist.push(2)\nlist.pop() # => 2\nlist.pop() # => 1\n```","summary":"

Adds a value to the end of a linked list.

","abstract":false,"args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"args_string":"(value : A)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L57","def":{"name":"push","args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"append(value)"}},{"id":"reverse-instance-method","html_id":"reverse-instance-method","name":"reverse","doc":"Creates a copy of the `LinkedList` with the order reversed.\n\n```\nlist = LinkedList(Int32).new(1, 2, 3)\nreversed_list = list.reverse\nlist.pop() # => 1\nlist.pop() # => 2\nlist.pop() # => 3\n```","summary":"

Creates a copy of the LinkedList with the order reversed.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L238","def":{"name":"reverse","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"LinkedList(A).new.tap do |new_list|\n each do |value|\n new_list.unshift(value.not_nil!)\n end\nend"}},{"id":"shift-instance-method","html_id":"shift-instance-method","name":"shift","doc":"Returns the first `Node` from the list and removes it.\n\n```\nlist = LinkedList(Float64).new(1.23)\nlist.push(4.56)\nlist.shift() # => 1.23\nlist.peek() # => 4.56\n```","summary":"

Returns the first Node from the list and removes it.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L116","def":{"name":"shift","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"if @head.next.nil?\n return\nend\nfirst = @head.next.not_nil!\n@head.next = first.next\nfirst.value\n"}},{"id":"unshift(value:A)-instance-method","html_id":"unshift(value:A)-instance-method","name":"unshift","doc":"Adds a *value* to the beginning of a linked list.\n\n```\nlist = LinkedList(Int32).new(1)\nlist.unshift(2)\nlist.pop() # => 1\nlist.pop() # => 2\n```","summary":"

Adds a value to the beginning of a linked list.

","abstract":false,"args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"args_string":"(value : A)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L98","def":{"name":"unshift","args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"new_top = Node(A).new(value)\nif @tail == @head\n @tail = new_top\nend\nnew_top.next = @head.next\n@head.next = new_top\nnew_top.value\n"}}],"macros":[],"types":[{"html_id":"github.com/reduced-reptile/crystal-linked-list/LinkedList/Node","path":"LinkedList/Node.html","kind":"class","full_name":"LinkedList::Node(T)","name":"Node","abstract":false,"superclass":{"html_id":"github.com/reduced-reptile/crystal-linked-list/Reference","kind":"class","full_name":"Reference","name":"Reference"},"ancestors":[{"html_id":"github.com/reduced-reptile/crystal-linked-list/Reference","kind":"class","full_name":"Reference","name":"Reference"},{"html_id":"github.com/reduced-reptile/crystal-linked-list/Object","kind":"class","full_name":"Object","name":"Object"}],"locations":[{"filename":"linked_list.cr","line_number":308,"url":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr"}],"repository_name":"github.com/reduced-reptile/crystal-linked-list","program":false,"enum":false,"alias":false,"aliased":"","const":false,"constants":[],"included_modules":[],"extended_modules":[],"subclasses":[],"including_types":[],"namespace":{"html_id":"github.com/reduced-reptile/crystal-linked-list/LinkedList","kind":"class","full_name":"LinkedList(A)","name":"LinkedList"},"doc":"A node is the building block of linked lists consisting of a values\nand a pointer to the next node in the linked list.\n\nTo create a node:\n\n```\nnode = Node.new(5)\nputs node.value\n```\n\nThe above produces:\n\n```text\n5\n```\n\nCheck the value of the node with `#value`.\nGet the next node in the list with `#next`.","summary":"

A node is the building block of linked lists consisting of a values and a pointer to the next node in the linked list.

","class_methods":[],"constructors":[{"id":"new(value:T)-class-method","html_id":"new(value:T)-class-method","name":"new","doc":"Creates a node with the specified *value*.","summary":"

Creates a node with the specified value.

","abstract":false,"args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"T"}],"args_string":"(value : T)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L317","def":{"name":"new","args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"T"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"_ = Node(T).allocate\n_.initialize(value)\nif _.responds_to?(:finalize)\n ::GC.add_finalizer(_)\nend\n_\n"}},{"id":"new-class-method","html_id":"new-class-method","name":"new","doc":"Creates a node with no value.","summary":"

Creates a node with no value.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L313","def":{"name":"new","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"_ = Node(T).allocate\n_.initialize\nif _.responds_to?(:finalize)\n ::GC.add_finalizer(_)\nend\n_\n"}}],"instance_methods":[{"id":"next-instance-method","html_id":"next-instance-method","name":"next","doc":"Returns the next node in the linked list, or nil if it is the tail.\n\n```\nnode = Node.new(1)\nnode.next = Node.new(2)\nnode.next.value # => 2\n```","summary":"

Returns the next node in the linked list, or nil if it is the tail.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L336","def":{"name":"next","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"@next"}},{"id":"next=(next_node:LinkedList::Node(T)?)-instance-method","html_id":"next=(next_node:LinkedList::Node(T)?)-instance-method","name":"next=","doc":"Sets the next node in the linked list to *next_node*\n\n```\nnode = Node.new(1)\nnode.next = Node.new(2)\nnode.next.value # => 2\n```","summary":"

Sets the next node in the linked list to next_node

","abstract":false,"args":[{"name":"next_node","doc":null,"default_value":"","external_name":"next_node","restriction":"Node(T) | Nil"}],"args_string":"(next_node : LinkedList::Node(T)?)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L347","def":{"name":"next=","args":[{"name":"next_node","doc":null,"default_value":"","external_name":"next_node","restriction":"Node(T) | Nil"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"@next = next_node"}},{"id":"value-instance-method","html_id":"value-instance-method","name":"value","doc":"Returns the value of the node, or nil if no value was supplied\n\n```\nNode.new(1).value # => 1\n```","summary":"

Returns the value of the node, or nil if no value was supplied

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L325","def":{"name":"value","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"@value"}}],"macros":[],"types":[]}]}]}} -------------------------------------------------------------------------------- /docs/js/doc.js: -------------------------------------------------------------------------------- 1 | window.CrystalDoc = (window.CrystalDoc || {}); 2 | 3 | CrystalDoc.base_path = (CrystalDoc.base_path || ""); 4 | 5 | CrystalDoc.searchIndex = (CrystalDoc.searchIndex || false); 6 | CrystalDoc.MAX_RESULTS_DISPLAY = 140; 7 | 8 | CrystalDoc.runQuery = function(query) { 9 | function searchType(type, query, results) { 10 | var matches = []; 11 | var matchedFields = []; 12 | var name = type.full_name; 13 | var i = name.lastIndexOf("::"); 14 | if (i > 0) { 15 | name = name.substring(i + 2); 16 | } 17 | var nameMatches = query.matches(name); 18 | if (nameMatches){ 19 | matches = matches.concat(nameMatches); 20 | matchedFields.push("name"); 21 | } 22 | 23 | var namespaceMatches = query.matchesNamespace(type.full_name); 24 | if(namespaceMatches){ 25 | matches = matches.concat(namespaceMatches); 26 | matchedFields.push("name"); 27 | } 28 | 29 | var docMatches = query.matches(type.doc); 30 | if(docMatches){ 31 | matches = matches.concat(docMatches); 32 | matchedFields.push("doc"); 33 | } 34 | if (matches.length > 0) { 35 | results.push({ 36 | id: type.id, 37 | result_type: "type", 38 | kind: type.kind, 39 | name: name, 40 | full_name: type.full_name, 41 | href: type.path, 42 | summary: type.summary, 43 | matched_fields: matchedFields, 44 | matched_terms: matches 45 | }); 46 | } 47 | 48 | type.instance_methods.forEach(function(method) { 49 | searchMethod(method, type, "instance_method", query, results); 50 | }) 51 | type.class_methods.forEach(function(method) { 52 | searchMethod(method, type, "class_method", query, results); 53 | }) 54 | type.constructors.forEach(function(constructor) { 55 | searchMethod(constructor, type, "constructor", query, results); 56 | }) 57 | type.macros.forEach(function(macro) { 58 | searchMethod(macro, type, "macro", query, results); 59 | }) 60 | type.constants.forEach(function(constant){ 61 | searchConstant(constant, type, query, results); 62 | }); 63 | 64 | type.types.forEach(function(subtype){ 65 | searchType(subtype, query, results); 66 | }); 67 | }; 68 | 69 | function searchMethod(method, type, kind, query, results) { 70 | var matches = []; 71 | var matchedFields = []; 72 | var nameMatches = query.matchesMethod(method.name, kind, type); 73 | if (nameMatches){ 74 | matches = matches.concat(nameMatches); 75 | matchedFields.push("name"); 76 | } 77 | 78 | method.args.forEach(function(arg){ 79 | var argMatches = query.matches(arg.external_name); 80 | if (argMatches) { 81 | matches = matches.concat(argMatches); 82 | matchedFields.push("args"); 83 | } 84 | }); 85 | 86 | var docMatches = query.matches(type.doc); 87 | if(docMatches){ 88 | matches = matches.concat(docMatches); 89 | matchedFields.push("doc"); 90 | } 91 | 92 | if (matches.length > 0) { 93 | var typeMatches = query.matches(type.full_name); 94 | if (typeMatches) { 95 | matchedFields.push("type"); 96 | matches = matches.concat(typeMatches); 97 | } 98 | results.push({ 99 | id: method.id, 100 | type: type.full_name, 101 | result_type: kind, 102 | name: method.name, 103 | full_name: type.full_name + "#" + method.name, 104 | args_string: method.args_string, 105 | summary: method.summary, 106 | href: type.path + "#" + method.id, 107 | matched_fields: matchedFields, 108 | matched_terms: matches 109 | }); 110 | } 111 | } 112 | 113 | function searchConstant(constant, type, query, results) { 114 | var matches = []; 115 | var matchedFields = []; 116 | var nameMatches = query.matches(constant.name); 117 | if (nameMatches){ 118 | matches = matches.concat(nameMatches); 119 | matchedFields.push("name"); 120 | } 121 | var docMatches = query.matches(constant.doc); 122 | if(docMatches){ 123 | matches = matches.concat(docMatches); 124 | matchedFields.push("doc"); 125 | } 126 | if (matches.length > 0) { 127 | var typeMatches = query.matches(type.full_name); 128 | if (typeMatches) { 129 | matchedFields.push("type"); 130 | matches = matches.concat(typeMatches); 131 | } 132 | results.push({ 133 | id: constant.id, 134 | type: type.full_name, 135 | result_type: "constant", 136 | name: constant.name, 137 | full_name: type.full_name + "#" + constant.name, 138 | value: constant.value, 139 | summary: constant.summary, 140 | href: type.path + "#" + constant.id, 141 | matched_fields: matchedFields, 142 | matched_terms: matches 143 | }); 144 | } 145 | } 146 | 147 | var results = []; 148 | searchType(CrystalDoc.searchIndex.program, query, results); 149 | return results; 150 | }; 151 | 152 | CrystalDoc.rankResults = function(results, query) { 153 | function uniqueArray(ar) { 154 | var j = {}; 155 | 156 | ar.forEach(function(v) { 157 | j[v + "::" + typeof v] = v; 158 | }); 159 | 160 | return Object.keys(j).map(function(v) { 161 | return j[v]; 162 | }); 163 | } 164 | 165 | results = results.sort(function(a, b) { 166 | var matchedTermsDiff = uniqueArray(b.matched_terms).length - uniqueArray(a.matched_terms).length; 167 | var aHasDocs = b.matched_fields.includes("doc"); 168 | var bHasDocs = b.matched_fields.includes("doc"); 169 | 170 | var aOnlyDocs = aHasDocs && a.matched_fields.length == 1; 171 | var bOnlyDocs = bHasDocs && b.matched_fields.length == 1; 172 | 173 | if (a.result_type == "type" && b.result_type != "type" && !aOnlyDocs) { 174 | if(CrystalDoc.DEBUG) { console.log("a is type b not"); } 175 | return -1; 176 | } else if (b.result_type == "type" && a.result_type != "type" && !bOnlyDocs) { 177 | if(CrystalDoc.DEBUG) { console.log("b is type, a not"); } 178 | return 1; 179 | } 180 | if (a.matched_fields.includes("name")) { 181 | if (b.matched_fields.includes("name")) { 182 | var a_name = (CrystalDoc.prefixForType(a.result_type) || "") + ((a.result_type == "type") ? a.full_name : a.name); 183 | var b_name = (CrystalDoc.prefixForType(b.result_type) || "") + ((b.result_type == "type") ? b.full_name : b.name); 184 | a_name = a_name.toLowerCase(); 185 | b_name = b_name.toLowerCase(); 186 | for(var i = 0; i < query.normalizedTerms.length; i++) { 187 | var term = query.terms[i].replace(/^::?|::?$/, ""); 188 | var a_orig_index = a_name.indexOf(term); 189 | var b_orig_index = b_name.indexOf(term); 190 | if(CrystalDoc.DEBUG) { console.log("term: " + term + " a: " + a_name + " b: " + b_name); } 191 | if(CrystalDoc.DEBUG) { console.log(a_orig_index, b_orig_index, a_orig_index - b_orig_index); } 192 | if (a_orig_index >= 0) { 193 | if (b_orig_index >= 0) { 194 | if(CrystalDoc.DEBUG) { console.log("both have exact match", a_orig_index > b_orig_index ? -1 : 1); } 195 | if(a_orig_index != b_orig_index) { 196 | if(CrystalDoc.DEBUG) { console.log("both have exact match at different positions", a_orig_index > b_orig_index ? 1 : -1); } 197 | return a_orig_index > b_orig_index ? 1 : -1; 198 | } 199 | } else { 200 | if(CrystalDoc.DEBUG) { console.log("a has exact match, b not"); } 201 | return -1; 202 | } 203 | } else if (b_orig_index >= 0) { 204 | if(CrystalDoc.DEBUG) { console.log("b has exact match, a not"); } 205 | return 1; 206 | } 207 | } 208 | } else { 209 | if(CrystalDoc.DEBUG) { console.log("a has match in name, b not"); } 210 | return -1; 211 | } 212 | } else if ( 213 | !a.matched_fields.includes("name") && 214 | b.matched_fields.includes("name") 215 | ) { 216 | return 1; 217 | } 218 | 219 | if (matchedTermsDiff != 0 || (aHasDocs != bHasDocs)) { 220 | if(CrystalDoc.DEBUG) { console.log("matchedTermsDiff: " + matchedTermsDiff, aHasDocs, bHasDocs); } 221 | return matchedTermsDiff; 222 | } 223 | 224 | var matchedFieldsDiff = b.matched_fields.length - a.matched_fields.length; 225 | if (matchedFieldsDiff != 0) { 226 | if(CrystalDoc.DEBUG) { console.log("matched to different number of fields: " + matchedFieldsDiff); } 227 | return matchedFieldsDiff > 0 ? 1 : -1; 228 | } 229 | 230 | var nameCompare = a.name.localeCompare(b.name); 231 | if(nameCompare != 0){ 232 | if(CrystalDoc.DEBUG) { console.log("nameCompare resulted in: " + a.name + "<=>" + b.name + ": " + nameCompare); } 233 | return nameCompare > 0 ? 1 : -1; 234 | } 235 | 236 | if(a.matched_fields.includes("args") && b.matched_fields.includes("args")) { 237 | for(var i = 0; i < query.terms.length; i++) { 238 | var term = query.terms[i]; 239 | var aIndex = a.args_string.indexOf(term); 240 | var bIndex = b.args_string.indexOf(term); 241 | if(CrystalDoc.DEBUG) { console.log("index of " + term + " in args_string: " + aIndex + " - " + bIndex); } 242 | if(aIndex >= 0){ 243 | if(bIndex >= 0){ 244 | if(aIndex != bIndex){ 245 | return aIndex > bIndex ? 1 : -1; 246 | } 247 | }else{ 248 | return -1; 249 | } 250 | }else if(bIndex >= 0) { 251 | return 1; 252 | } 253 | } 254 | } 255 | 256 | return 0; 257 | }); 258 | 259 | if (results.length > 1) { 260 | // if we have more than two search terms, only include results with the most matches 261 | var bestMatchedTerms = uniqueArray(results[0].matched_terms).length; 262 | 263 | results = results.filter(function(result) { 264 | return uniqueArray(result.matched_terms).length + 1 >= bestMatchedTerms; 265 | }); 266 | } 267 | return results; 268 | }; 269 | 270 | CrystalDoc.prefixForType = function(type) { 271 | switch (type) { 272 | case "instance_method": 273 | return "#"; 274 | 275 | case "class_method": 276 | case "macro": 277 | case "constructor": 278 | return "."; 279 | 280 | default: 281 | return false; 282 | } 283 | }; 284 | 285 | CrystalDoc.displaySearchResults = function(results, query) { 286 | function sanitize(html){ 287 | return html.replace(/<(?!\/?code)[^>]+>/g, ""); 288 | } 289 | 290 | // limit results 291 | if (results.length > CrystalDoc.MAX_RESULTS_DISPLAY) { 292 | results = results.slice(0, CrystalDoc.MAX_RESULTS_DISPLAY); 293 | } 294 | 295 | var $frag = document.createDocumentFragment(); 296 | var $resultsElem = document.querySelector(".search-list"); 297 | $resultsElem.innerHTML = ""; 298 | 299 | results.forEach(function(result, i) { 300 | var url = CrystalDoc.base_path + result.href; 301 | var type = false; 302 | 303 | var title = query.highlight(result.result_type == "type" ? result.full_name : result.name); 304 | 305 | var prefix = CrystalDoc.prefixForType(result.result_type); 306 | if (prefix) { 307 | title = "" + prefix + "" + title; 308 | } 309 | 310 | title = "" + title + ""; 311 | 312 | if (result.args_string) { 313 | title += 314 | "" + query.highlight(result.args_string) + ""; 315 | } 316 | 317 | $elem = document.createElement("li"); 318 | $elem.className = "search-result search-result--" + result.result_type; 319 | $elem.dataset.href = url; 320 | $elem.setAttribute("title", result.full_name + " docs page"); 321 | 322 | var $title = document.createElement("div"); 323 | $title.setAttribute("class", "search-result__title"); 324 | var $titleLink = document.createElement("a"); 325 | $titleLink.setAttribute("href", url); 326 | 327 | $titleLink.innerHTML = title; 328 | $title.appendChild($titleLink); 329 | $elem.appendChild($title); 330 | $elem.addEventListener("click", function() { 331 | $titleLink.click(); 332 | }); 333 | 334 | if (result.result_type !== "type") { 335 | var $type = document.createElement("div"); 336 | $type.setAttribute("class", "search-result__type"); 337 | $type.innerHTML = query.highlight(result.type); 338 | $elem.appendChild($type); 339 | } 340 | 341 | if(result.summary){ 342 | var $doc = document.createElement("div"); 343 | $doc.setAttribute("class", "search-result__doc"); 344 | $doc.innerHTML = query.highlight(sanitize(result.summary)); 345 | $elem.appendChild($doc); 346 | } 347 | 348 | $elem.appendChild(document.createComment(JSON.stringify(result))); 349 | $frag.appendChild($elem); 350 | }); 351 | 352 | $resultsElem.appendChild($frag); 353 | 354 | CrystalDoc.toggleResultsList(true); 355 | }; 356 | 357 | CrystalDoc.toggleResultsList = function(visible) { 358 | if (visible) { 359 | document.querySelector(".types-list").classList.add("hidden"); 360 | document.querySelector(".search-results").classList.remove("hidden"); 361 | } else { 362 | document.querySelector(".types-list").classList.remove("hidden"); 363 | document.querySelector(".search-results").classList.add("hidden"); 364 | } 365 | }; 366 | 367 | CrystalDoc.Query = function(string) { 368 | this.original = string; 369 | this.terms = string.split(/\s+/).filter(function(word) { 370 | return CrystalDoc.Query.stripModifiers(word).length > 0; 371 | }); 372 | 373 | var normalized = this.terms.map(CrystalDoc.Query.normalizeTerm); 374 | this.normalizedTerms = normalized; 375 | 376 | function runMatcher(field, matcher) { 377 | if (!field) { 378 | return false; 379 | } 380 | var normalizedValue = CrystalDoc.Query.normalizeTerm(field); 381 | 382 | var matches = []; 383 | normalized.forEach(function(term) { 384 | if (matcher(normalizedValue, term)) { 385 | matches.push(term); 386 | } 387 | }); 388 | return matches.length > 0 ? matches : false; 389 | } 390 | 391 | this.matches = function(field) { 392 | return runMatcher(field, function(normalized, term) { 393 | if (term[0] == "#" || term[0] == ".") { 394 | return false; 395 | } 396 | return normalized.indexOf(term) >= 0; 397 | }); 398 | }; 399 | 400 | function namespaceMatcher(normalized, term){ 401 | var i = term.indexOf(":"); 402 | if(i >= 0){ 403 | term = term.replace(/^::?|::?$/, ""); 404 | var index = normalized.indexOf(term); 405 | if((index == 0) || (index > 0 && normalized[index-1] == ":")){ 406 | return true; 407 | } 408 | } 409 | return false; 410 | } 411 | this.matchesMethod = function(name, kind, type) { 412 | return runMatcher(name, function(normalized, term) { 413 | var i = term.indexOf("#"); 414 | if(i >= 0){ 415 | if (kind != "instance_method") { 416 | return false; 417 | } 418 | }else{ 419 | i = term.indexOf("."); 420 | if(i >= 0){ 421 | if (kind != "class_method" && kind != "macro" && kind != "constructor") { 422 | return false; 423 | } 424 | }else{ 425 | //neither # nor . 426 | if(term.indexOf(":") && namespaceMatcher(normalized, term)){ 427 | return true; 428 | } 429 | } 430 | } 431 | 432 | var methodName = term; 433 | if(i >= 0){ 434 | var termType = term.substring(0, i); 435 | methodName = term.substring(i+1); 436 | 437 | if(termType != "") { 438 | if(CrystalDoc.Query.normalizeTerm(type.full_name).indexOf(termType) < 0){ 439 | return false; 440 | } 441 | } 442 | } 443 | return normalized.indexOf(methodName) >= 0; 444 | }); 445 | }; 446 | 447 | this.matchesNamespace = function(namespace){ 448 | return runMatcher(namespace, namespaceMatcher); 449 | }; 450 | 451 | this.highlight = function(string) { 452 | if (typeof string == "undefined") { 453 | return ""; 454 | } 455 | function escapeRegExp(s) { 456 | return s.replace(/[.*+?\^${}()|\[\]\\]/g, "\\$&").replace(/^[#\.:]+/, ""); 457 | } 458 | return string.replace( 459 | new RegExp("(" + this.normalizedTerms.map(escapeRegExp).join("|") + ")", "gi"), 460 | "$1" 461 | ); 462 | }; 463 | }; 464 | CrystalDoc.Query.normalizeTerm = function(term) { 465 | return term.toLowerCase(); 466 | }; 467 | CrystalDoc.Query.stripModifiers = function(term) { 468 | switch (term[0]) { 469 | case "#": 470 | case ".": 471 | case ":": 472 | return term.substr(1); 473 | 474 | default: 475 | return term; 476 | } 477 | } 478 | 479 | CrystalDoc.search = function(string) { 480 | if(!CrystalDoc.searchIndex) { 481 | console.log("CrystalDoc search index not initialized, delaying search"); 482 | 483 | document.addEventListener("CrystalDoc:loaded", function listener(){ 484 | document.removeEventListener("CrystalDoc:loaded", listener); 485 | CrystalDoc.search(string); 486 | }); 487 | return; 488 | } 489 | 490 | document.dispatchEvent(new Event("CrystalDoc:searchStarted")); 491 | 492 | var query = new CrystalDoc.Query(string); 493 | var results = CrystalDoc.runQuery(query); 494 | results = CrystalDoc.rankResults(results, query); 495 | CrystalDoc.displaySearchResults(results, query); 496 | 497 | document.dispatchEvent(new Event("CrystalDoc:searchPerformed")); 498 | }; 499 | 500 | CrystalDoc.initializeIndex = function(data) { 501 | CrystalDoc.searchIndex = data; 502 | 503 | document.dispatchEvent(new Event("CrystalDoc:loaded")); 504 | }; 505 | 506 | CrystalDoc.loadIndex = function() { 507 | function loadJSON(file, callback) { 508 | var xobj = new XMLHttpRequest(); 509 | xobj.overrideMimeType("application/json"); 510 | xobj.open("GET", file, true); 511 | xobj.onreadystatechange = function() { 512 | if (xobj.readyState == 4 && xobj.status == "200") { 513 | callback(xobj.responseText); 514 | } 515 | }; 516 | xobj.send(null); 517 | } 518 | 519 | function loadScript(file) { 520 | script = document.createElement("script"); 521 | script.src = file; 522 | document.body.appendChild(script); 523 | } 524 | 525 | function parseJSON(json) { 526 | CrystalDoc.initializeIndex(JSON.parse(json)); 527 | } 528 | 529 | for(var i = 0; i < document.scripts.length; i++){ 530 | var script = document.scripts[i]; 531 | if (script.src && script.src.indexOf("js/doc.js") >= 0) { 532 | if (script.src.indexOf("file://") == 0) { 533 | // We need to support JSONP files for the search to work on local file system. 534 | var jsonPath = script.src.replace("js/doc.js", "search-index.js"); 535 | loadScript(jsonPath); 536 | return; 537 | } else { 538 | var jsonPath = script.src.replace("js/doc.js", "index.json"); 539 | loadJSON(jsonPath, parseJSON); 540 | return; 541 | } 542 | } 543 | } 544 | console.error("Could not find location of js/doc.js"); 545 | }; 546 | 547 | // Callback for jsonp 548 | function crystal_doc_search_index_callback(data) { 549 | CrystalDoc.initializeIndex(data); 550 | } 551 | 552 | Navigator = function(sidebar, searchInput, list, leaveSearchScope){ 553 | this.list = list; 554 | var self = this; 555 | 556 | var performingSearch = false; 557 | 558 | document.addEventListener('CrystalDoc:searchStarted', function(){ 559 | performingSearch = true; 560 | }); 561 | document.addEventListener('CrystalDoc:searchDebounceStarted', function(){ 562 | performingSearch = true; 563 | }); 564 | document.addEventListener('CrystalDoc:searchPerformed', function(){ 565 | performingSearch = false; 566 | }); 567 | document.addEventListener('CrystalDoc:searchDebounceStopped', function(event){ 568 | performingSearch = false; 569 | }); 570 | 571 | function delayWhileSearching(callback) { 572 | if(performingSearch){ 573 | document.addEventListener('CrystalDoc:searchPerformed', function listener(){ 574 | document.removeEventListener('CrystalDoc:searchPerformed', listener); 575 | 576 | // add some delay to let search results display kick in 577 | setTimeout(callback, 100); 578 | }); 579 | }else{ 580 | callback(); 581 | } 582 | } 583 | 584 | function clearMoveTimeout() { 585 | clearTimeout(self.moveTimeout); 586 | self.moveTimeout = null; 587 | } 588 | 589 | function startMoveTimeout(upwards){ 590 | /*if(self.moveTimeout) { 591 | clearMoveTimeout(); 592 | } 593 | 594 | var go = function() { 595 | if (!self.moveTimeout) return; 596 | self.move(upwards); 597 | self.moveTimout = setTimeout(go, 600); 598 | }; 599 | self.moveTimeout = setTimeout(go, 800);*/ 600 | } 601 | 602 | function scrollCenter(element) { 603 | var rect = element.getBoundingClientRect(); 604 | var middle = sidebar.clientHeight / 2; 605 | sidebar.scrollTop += rect.top + rect.height / 2 - middle; 606 | } 607 | 608 | var move = this.move = function(upwards){ 609 | if(!this.current){ 610 | this.highlightFirst(); 611 | return true; 612 | } 613 | var next = upwards ? this.current.previousElementSibling : this.current.nextElementSibling; 614 | if(next && next.classList) { 615 | this.highlight(next); 616 | scrollCenter(next); 617 | return true; 618 | } 619 | return false; 620 | }; 621 | 622 | this.moveRight = function(){ 623 | }; 624 | this.moveLeft = function(){ 625 | }; 626 | 627 | this.highlight = function(elem) { 628 | if(!elem){ 629 | return; 630 | } 631 | this.removeHighlight(); 632 | 633 | this.current = elem; 634 | this.current.classList.add("current"); 635 | }; 636 | 637 | this.highlightFirst = function(){ 638 | this.highlight(this.list.querySelector('li:first-child')); 639 | }; 640 | 641 | this.removeHighlight = function() { 642 | if(this.current){ 643 | this.current.classList.remove("current"); 644 | } 645 | this.current = null; 646 | } 647 | 648 | this.openSelectedResult = function() { 649 | if(this.current) { 650 | this.current.click(); 651 | } 652 | } 653 | 654 | this.focus = function() { 655 | searchInput.focus(); 656 | searchInput.select(); 657 | this.highlightFirst(); 658 | } 659 | 660 | function handleKeyUp(event) { 661 | switch(event.key) { 662 | case "ArrowUp": 663 | case "ArrowDown": 664 | case "i": 665 | case "j": 666 | case "k": 667 | case "l": 668 | case "c": 669 | case "h": 670 | case "t": 671 | case "n": 672 | event.stopPropagation(); 673 | clearMoveTimeout(); 674 | } 675 | } 676 | 677 | function handleKeyDown(event) { 678 | switch(event.key) { 679 | case "Enter": 680 | event.stopPropagation(); 681 | event.preventDefault(); 682 | leaveSearchScope(); 683 | self.openSelectedResult(); 684 | break; 685 | case "Escape": 686 | event.stopPropagation(); 687 | event.preventDefault(); 688 | leaveSearchScope(); 689 | break; 690 | case "j": 691 | case "c": 692 | case "ArrowUp": 693 | if(event.ctrlKey || event.key == "ArrowUp") { 694 | event.stopPropagation(); 695 | self.move(true); 696 | startMoveTimeout(true); 697 | } 698 | break; 699 | case "k": 700 | case "h": 701 | case "ArrowDown": 702 | if(event.ctrlKey || event.key == "ArrowDown") { 703 | event.stopPropagation(); 704 | self.move(false); 705 | startMoveTimeout(false); 706 | } 707 | break; 708 | case "k": 709 | case "t": 710 | case "ArrowLeft": 711 | if(event.ctrlKey || event.key == "ArrowLeft") { 712 | event.stopPropagation(); 713 | self.moveLeft(); 714 | } 715 | break; 716 | case "l": 717 | case "n": 718 | case "ArrowRight": 719 | if(event.ctrlKey || event.key == "ArrowRight") { 720 | event.stopPropagation(); 721 | self.moveRight(); 722 | } 723 | break; 724 | } 725 | } 726 | 727 | function handleInputKeyUp(event) { 728 | switch(event.key) { 729 | case "ArrowUp": 730 | case "ArrowDown": 731 | event.stopPropagation(); 732 | event.preventDefault(); 733 | clearMoveTimeout(); 734 | } 735 | } 736 | 737 | function handleInputKeyDown(event) { 738 | switch(event.key) { 739 | case "Enter": 740 | event.stopPropagation(); 741 | event.preventDefault(); 742 | delayWhileSearching(function(){ 743 | self.openSelectedResult(); 744 | leaveSearchScope(); 745 | }); 746 | break; 747 | case "Escape": 748 | event.stopPropagation(); 749 | event.preventDefault(); 750 | // remove focus from search input 751 | leaveSearchScope(); 752 | sidebar.focus(); 753 | break; 754 | case "ArrowUp": 755 | event.stopPropagation(); 756 | event.preventDefault(); 757 | self.move(true); 758 | startMoveTimeout(true); 759 | break; 760 | 761 | case "ArrowDown": 762 | event.stopPropagation(); 763 | event.preventDefault(); 764 | self.move(false); 765 | startMoveTimeout(false); 766 | break; 767 | } 768 | } 769 | 770 | sidebar.tabIndex = 100; // set tabIndex to enable keylistener 771 | sidebar.addEventListener('keyup', function(event) { 772 | handleKeyUp(event); 773 | }); 774 | sidebar.addEventListener('keydown', function(event) { 775 | handleKeyDown(event); 776 | }); 777 | searchInput.addEventListener('keydown', function(event) { 778 | handleInputKeyDown(event); 779 | }); 780 | searchInput.addEventListener('keyup', function(event) { 781 | handleInputKeyUp(event); 782 | }); 783 | this.move(); 784 | }; 785 | 786 | var UsageModal = function(title, content) { 787 | var $body = document.body; 788 | var self = this; 789 | var $modalBackground = document.createElement("div"); 790 | $modalBackground.classList.add("modal-background"); 791 | var $usageModal = document.createElement("div"); 792 | $usageModal.classList.add("usage-modal"); 793 | $modalBackground.appendChild($usageModal); 794 | var $title = document.createElement("h3"); 795 | $title.classList.add("modal-title"); 796 | $title.innerHTML = title 797 | $usageModal.appendChild($title); 798 | var $closeButton = document.createElement("span"); 799 | $closeButton.classList.add("close-button"); 800 | $closeButton.setAttribute("title", "Close modal"); 801 | $closeButton.innerText = '×'; 802 | $usageModal.appendChild($closeButton); 803 | $usageModal.insertAdjacentHTML("beforeend", content); 804 | 805 | $modalBackground.addEventListener('click', function(event) { 806 | var element = event.target || event.srcElement; 807 | 808 | if(element == $modalBackground) { 809 | self.hide(); 810 | } 811 | }); 812 | $closeButton.addEventListener('click', function(event) { 813 | self.hide(); 814 | }); 815 | 816 | $body.insertAdjacentElement('beforeend', $modalBackground); 817 | 818 | this.show = function(){ 819 | $body.classList.add("js-modal-visible"); 820 | }; 821 | this.hide = function(){ 822 | $body.classList.remove("js-modal-visible"); 823 | }; 824 | this.isVisible = function(){ 825 | return $body.classList.contains("js-modal-visible"); 826 | } 827 | } 828 | 829 | 830 | document.addEventListener('DOMContentLoaded', function() { 831 | var sessionStorage; 832 | try { 833 | sessionStorage = window.sessionStorage; 834 | } catch (e) { } 835 | if(!sessionStorage) { 836 | sessionStorage = { 837 | setItem: function() {}, 838 | getItem: function() {}, 839 | removeItem: function() {} 840 | }; 841 | } 842 | 843 | var repositoryName = document.querySelector('#repository-name').getAttribute('content'); 844 | var typesList = document.querySelector('.types-list'); 845 | var searchInput = document.querySelector('.search-input'); 846 | var parents = document.querySelectorAll('.types-list li.parent'); 847 | 848 | var scrollSidebarToOpenType = function(){ 849 | var openTypes = typesList.querySelectorAll('.current'); 850 | if (openTypes.length > 0) { 851 | var lastOpenType = openTypes[openTypes.length - 1]; 852 | lastOpenType.scrollIntoView(); 853 | } 854 | } 855 | 856 | scrollSidebarToOpenType(); 857 | 858 | var setPersistentSearchQuery = function(value){ 859 | sessionStorage.setItem(repositoryName + '::search-input:value', value); 860 | } 861 | 862 | for(var i = 0; i < parents.length; i++) { 863 | var _parent = parents[i]; 864 | _parent.addEventListener('click', function(e) { 865 | e.stopPropagation(); 866 | 867 | if(e.target.tagName.toLowerCase() == 'li') { 868 | if(e.target.className.match(/open/)) { 869 | sessionStorage.removeItem(e.target.getAttribute('data-id')); 870 | e.target.className = e.target.className.replace(/ +open/g, ''); 871 | } else { 872 | sessionStorage.setItem(e.target.getAttribute('data-id'), '1'); 873 | if(e.target.className.indexOf('open') == -1) { 874 | e.target.className += ' open'; 875 | } 876 | } 877 | } 878 | }); 879 | 880 | if(sessionStorage.getItem(_parent.getAttribute('data-id')) == '1') { 881 | _parent.className += ' open'; 882 | } 883 | } 884 | 885 | var leaveSearchScope = function(){ 886 | CrystalDoc.toggleResultsList(false); 887 | window.focus(); 888 | } 889 | 890 | var navigator = new Navigator(document.querySelector('.types-list'), searchInput, document.querySelector(".search-results"), leaveSearchScope); 891 | 892 | CrystalDoc.loadIndex(); 893 | var searchTimeout; 894 | var lastSearchText = false; 895 | var performSearch = function() { 896 | document.dispatchEvent(new Event("CrystalDoc:searchDebounceStarted")); 897 | 898 | clearTimeout(searchTimeout); 899 | searchTimeout = setTimeout(function() { 900 | var text = searchInput.value; 901 | 902 | if(text == "") { 903 | CrystalDoc.toggleResultsList(false); 904 | }else if(text == lastSearchText){ 905 | document.dispatchEvent(new Event("CrystalDoc:searchDebounceStopped")); 906 | }else{ 907 | CrystalDoc.search(text); 908 | navigator.highlightFirst(); 909 | searchInput.focus(); 910 | } 911 | lastSearchText = text; 912 | setPersistentSearchQuery(text); 913 | }, 200); 914 | }; 915 | 916 | if(location.hash.length > 3 && location.hash.substring(0,3) == "#q="){ 917 | // allows directly linking a search query which is then executed on the client 918 | // this comes handy for establishing a custom browser search engine with https://crystal-lang.org/api/#q=%s as a search URL 919 | // TODO: Add OpenSearch description 920 | var searchQuery = location.hash.substring(3); 921 | history.pushState({searchQuery: searchQuery}, "Search for " + searchQuery, location.href.replace(/#q=.*/, "")); 922 | searchInput.value = searchQuery; 923 | document.addEventListener('CrystalDoc:loaded', performSearch); 924 | } 925 | 926 | if (searchInput.value.length == 0) { 927 | var searchText = sessionStorage.getItem(repositoryName + '::search-input:value'); 928 | if(searchText){ 929 | searchInput.value = searchText; 930 | } 931 | } 932 | searchInput.addEventListener('keyup', performSearch); 933 | searchInput.addEventListener('input', performSearch); 934 | 935 | var usageModal = new UsageModal('Keyboard Shortcuts', '' + 936 | '' 974 | ); 975 | 976 | function handleShortkeys(event) { 977 | var element = event.target || event.srcElement; 978 | 979 | if(element.tagName == "INPUT" || element.tagName == "TEXTAREA" || element.parentElement.tagName == "TEXTAREA"){ 980 | return; 981 | } 982 | 983 | switch(event.key) { 984 | case "?": 985 | usageModal.show(); 986 | break; 987 | 988 | case "Escape": 989 | usageModal.hide(); 990 | break; 991 | 992 | case "s": 993 | case "/": 994 | if(usageModal.isVisible()) { 995 | return; 996 | } 997 | event.stopPropagation(); 998 | navigator.focus(); 999 | performSearch(); 1000 | break; 1001 | } 1002 | } 1003 | 1004 | document.addEventListener('keyup', handleShortkeys); 1005 | 1006 | var scrollToEntryFromLocationHash = function() { 1007 | var hash = window.location.hash; 1008 | if (hash) { 1009 | var targetAnchor = unescape(hash.substr(1)); 1010 | var targetEl = document.querySelectorAll('.entry-detail[id="' + targetAnchor + '"]'); 1011 | 1012 | if (targetEl && targetEl.length > 0) { 1013 | targetEl[0].offsetParent.scrollTop = targetEl[0].offsetTop; 1014 | } 1015 | } 1016 | }; 1017 | window.addEventListener("hashchange", scrollToEntryFromLocationHash, false); 1018 | scrollToEntryFromLocationHash(); 1019 | }); 1020 | -------------------------------------------------------------------------------- /docs/search-index.js: -------------------------------------------------------------------------------- 1 | crystal_doc_search_index_callback({"repository_name":"github.com/reduced-reptile/crystal-linked-list","body":"# Crystal Linked List [![CircleCI](https://circleci.com/gh/abvdasker/crystal-linked-list.svg?style=svg)](https://circleci.com/gh/abvdasker/crystal-linked-list)\n\nA simple linked list implementation in Crystal\n\n## Installation\n\nAdd this to a project's `shards.yml`\n\n```yml\ndependencies:\n linked_list:\n git: https://github.com/abvdasker/linked_list.git\n```\n\nThen run `crystal deps`\n\n## Usage\n\n```crystal\nrequire \"linked_list\"\n\nlist = LinkedList(Int32 | String).new\nlist.append(1)\nlist.push(2)\nlist << \"foo\"\n\nlist.peek # \"foo\"\nlist.pop # \"foo\"\nlist.pop # 2\nlist.unshift(1)\nlist.shift # 1\n```\n","program":{"html_id":"github.com/reduced-reptile/crystal-linked-list/toplevel","path":"toplevel.html","kind":"module","full_name":"Top Level Namespace","name":"Top Level Namespace","abstract":false,"superclass":null,"ancestors":[],"locations":[],"repository_name":"github.com/reduced-reptile/crystal-linked-list","program":true,"enum":false,"alias":false,"aliased":"","const":false,"constants":[],"included_modules":[],"extended_modules":[],"subclasses":[],"including_types":[],"namespace":null,"doc":null,"summary":null,"class_methods":[],"constructors":[],"instance_methods":[],"macros":[],"types":[{"html_id":"github.com/reduced-reptile/crystal-linked-list/LinkedList","path":"LinkedList.html","kind":"class","full_name":"LinkedList(A)","name":"LinkedList","abstract":false,"superclass":{"html_id":"github.com/reduced-reptile/crystal-linked-list/Reference","kind":"class","full_name":"Reference","name":"Reference"},"ancestors":[{"html_id":"github.com/reduced-reptile/crystal-linked-list/Enumerable","kind":"module","full_name":"Enumerable","name":"Enumerable"},{"html_id":"github.com/reduced-reptile/crystal-linked-list/Reference","kind":"class","full_name":"Reference","name":"Reference"},{"html_id":"github.com/reduced-reptile/crystal-linked-list/Object","kind":"class","full_name":"Object","name":"Object"}],"locations":[{"filename":"linked_list.cr","line_number":17,"url":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr"}],"repository_name":"github.com/reduced-reptile/crystal-linked-list","program":false,"enum":false,"alias":false,"aliased":"","const":false,"constants":[],"included_modules":[{"html_id":"github.com/reduced-reptile/crystal-linked-list/Enumerable","kind":"module","full_name":"Enumerable","name":"Enumerable"}],"extended_modules":[],"subclasses":[],"including_types":[],"namespace":null,"doc":"A linked list is a **Enumerable** (see the `Enumerable` module) data structure\nthat stores multiple pieces of data in non contiguous locations in memory.\n\nTo create a linked list:\n\n```\nlist = LinkedList(Int32).new\nlist.push(2)\nputs list.pop\n```\n\nThe above produces:\n\n```text\n2\n```","summary":"

A linked list is a Enumerable (see the Enumerable module) data structure that stores multiple pieces of data in non contiguous locations in memory.

","class_methods":[],"constructors":[{"id":"new(values:Enumerable(A))-class-method","html_id":"new(values:Enumerable(A))-class-method","name":"new","doc":"Creates a linked list with the *values* as the values of the nodes.","summary":"

Creates a linked list with the values as the values of the nodes.

","abstract":false,"args":[{"name":"values","doc":null,"default_value":"","external_name":"values","restriction":"Enumerable(A)"}],"args_string":"(values : Enumerable(A))","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L32","def":{"name":"new","args":[{"name":"values","doc":null,"default_value":"","external_name":"values","restriction":"Enumerable(A)"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"_ = LinkedList(A).allocate\n_.initialize(values)\nif _.responds_to?(:finalize)\n ::GC.add_finalizer(_)\nend\n_\n"}},{"id":"new-class-method","html_id":"new-class-method","name":"new","doc":"Creates an empty linked list.","summary":"

Creates an empty linked list.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L21","def":{"name":"new","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"_ = LinkedList(A).allocate\n_.initialize\nif _.responds_to?(:finalize)\n ::GC.add_finalizer(_)\nend\n_\n"}},{"id":"new(*values)-class-method","html_id":"new(*values)-class-method","name":"new","doc":"Creates a linked list with the *values* as the values of the nodes.","summary":"

Creates a linked list with the values as the values of the nodes.

","abstract":false,"args":[{"name":"values","doc":null,"default_value":"","external_name":"values","restriction":""}],"args_string":"(*values)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L27","def":{"name":"new","args":[{"name":"values","doc":null,"default_value":"","external_name":"values","restriction":""}],"double_splat":null,"splat_index":0,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"_ = LinkedList(A).allocate\n_.initialize(*values)\nif _.responds_to?(:finalize)\n ::GC.add_finalizer(_)\nend\n_\n"}}],"instance_methods":[{"id":"+(list:Enumerable(C))forallC-instance-method","html_id":"+(list:Enumerable(C))forallC-instance-method","name":"+","doc":"Returns a new `LinkedList` with all of the elements from the first list\nfollowed by all of the elements in the second *list*.\n\n```\nfirst_list = LinkedList(Int32).new(1, 2)\nsecond_list = LinkedList(String).new(\"foo\", \"bar\")\ncombined_list = first_list + second_list\ncombined_list.peek() # => \"bar\"\ncombined_list.shift() # => 1\n```","summary":"

Returns a new LinkedList with all of the elements from the first list followed by all of the elements in the second list.

","abstract":false,"args":[{"name":"list","doc":null,"default_value":"","external_name":"list","restriction":"Enumerable(C)"}],"args_string":"(list : Enumerable(C)) forall C","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L191","def":{"name":"+","args":[{"name":"list","doc":null,"default_value":"","external_name":"list","restriction":"Enumerable(C)"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"LinkedList(A | C).new.tap do |new_list|\n each do |value|\n new_list.append(value)\n end\n list.each do |value|\n new_list.append(value)\n end\nend"}},{"id":"<<(value:A)-instance-method","html_id":"<<(value:A)-instance-method","name":"<<","doc":"Override the << (shift) operator to add a *value* to the end of a\nlinked list. This method returns itself so it can be chained.\n\n```\nlist = LinkedList(Int32).new(1)\nlist << 2 << 3\nlist.pop() # => 3\nlist.pop() # => 2\nlist.pop() # => 1\n```","summary":"

Override the << (shift) operator to add a value to the end of a linked list.

","abstract":false,"args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"args_string":"(value : A)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L71","def":{"name":"<<","args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"append(value)\nself\n"}},{"id":"append(value:A)-instance-method","html_id":"append(value:A)-instance-method","name":"append","doc":"Adds a *value* to the end of a linked list.\n\n```\nlist = LinkedList(Int32).new\nlist.append(1)\nlist.push(2)\nlist.pop() # => 2\nlist.pop() # => 1\n```","summary":"

Adds a value to the end of a linked list.

","abstract":false,"args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"args_string":"(value : A)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L49","def":{"name":"append","args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"new_node = Node(A).new(value)\n@tail.next = new_node\n@tail = new_node\nnew_node.value\n"}},{"id":"append(*values)-instance-method","html_id":"append(*values)-instance-method","name":"append","doc":"Adds a list of *values* to the end of a linked list.\n\n```\nlist = LinkedList(Int32 | String).new\nlist.append(1, \"foo\")\nlist.pop() # => \"foo\"\nlist.pop() # => 1\n```","summary":"

Adds a list of values to the end of a linked list.

","abstract":false,"args":[{"name":"values","doc":null,"default_value":"","external_name":"values","restriction":""}],"args_string":"(*values)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L84","def":{"name":"append","args":[{"name":"values","doc":null,"default_value":"","external_name":"values","restriction":""}],"double_splat":null,"splat_index":0,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"values.each do |value|\n append(value)\nend"}},{"id":"concat(list:LinkedList(A))-instance-method","html_id":"concat(list:LinkedList(A))-instance-method","name":"concat","doc":"Adds all the elemenets of the *list* to the end of the current linked list.\n\n```\nfirst_list = LinkedList(Int32).new(1, 2)\nsecond_list = LinkedList(Int32).new(3, 4)\ncombined_list = first_list.concat(second_list)\ncombined_list.peek() # => 4\ncombined_list.shift() # => 1\n```","summary":"

Adds all the elemenets of the list to the end of the current linked list.

","abstract":false,"args":[{"name":"list","doc":null,"default_value":"","external_name":"list","restriction":"LinkedList(A)"}],"args_string":"(list : LinkedList(A))","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L211","def":{"name":"concat","args":[{"name":"list","doc":null,"default_value":"","external_name":"list","restriction":"LinkedList(A)"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"@tail.next = list.head.next\n@tail = list.tail\nself\n"}},{"id":"each(&block)-instance-method","html_id":"each(&block)-instance-method","name":"each","doc":"Iterates over all the values in the linked list.\n\n```\nvalues = [1, 2, 3]\nlist = LinkedList(Int32).new(values)\nlist.each do |elem|\n puts elem\nend\n\nThe above produces:\n\n```text\n1\n2\n3\n```","summary":"

Iterates over all the values in the linked list.

","abstract":false,"args":[],"args_string":"(&block)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L174","def":{"name":"each","args":[],"double_splat":null,"splat_index":null,"yields":1,"block_arg":null,"return_type":"","visibility":"Public","body":"each_node do |node|\n yield node.value\nend\nself\n"}},{"id":"empty?-instance-method","html_id":"empty?-instance-method","name":"empty?","doc":"Returns true if and only if there are no elements in the list.\n\n```\nlist = LinkedList(Int32).new\nlist.empty? # => true\nlist.push(1)\nlist.empty? # => false\n```","summary":"

Returns true if and only if there are no elements in the list.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L225","def":{"name":"empty?","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"@head == @tail"}},{"id":"peek-instance-method","html_id":"peek-instance-method","name":"peek","doc":"Returns the value of the tail of the linked list,\nor nil if no value was supplied.\n\n```\nlist = LinkedList(Float64).new(1.23)\nlist.push(4.56)\nlist.peek() # => 4.56\n```","summary":"

Returns the value of the tail of the linked list, or nil if no value was supplied.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L132","def":{"name":"peek","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"@tail.value"}},{"id":"pop-instance-method","html_id":"pop-instance-method","name":"pop","doc":"Returns the last `Node` from the list and removes it.\n\n```\nlist = LinkedList(Float64).new(1.23)\nlist.push(4.56)\nlist.pop() # => 4.56\nlist.peek() # => 1.23\n```","summary":"

Returns the last Node from the list and removes it.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L144","def":{"name":"pop","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"if @head == @tail\n return nil\nend\nlast = @tail\ncurrent = @head\nwhile current.next != last\n current = current.next.not_nil!\nend\ncurrent.next = nil\n@tail = current\nlast.value\n"}},{"id":"push(value:A)-instance-method","html_id":"push(value:A)-instance-method","name":"push","doc":"Adds a *value* to the end of a linked list.\n\n```\nlist = LinkedList(Int32).new\nlist.append(1)\nlist.push(2)\nlist.pop() # => 2\nlist.pop() # => 1\n```","summary":"

Adds a value to the end of a linked list.

","abstract":false,"args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"args_string":"(value : A)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L57","def":{"name":"push","args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"append(value)"}},{"id":"reverse-instance-method","html_id":"reverse-instance-method","name":"reverse","doc":"Creates a copy of the `LinkedList` with the order reversed.\n\n```\nlist = LinkedList(Int32).new(1, 2, 3)\nreversed_list = list.reverse\nlist.pop() # => 1\nlist.pop() # => 2\nlist.pop() # => 3\n```","summary":"

Creates a copy of the LinkedList with the order reversed.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L238","def":{"name":"reverse","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"LinkedList(A).new.tap do |new_list|\n each do |value|\n new_list.unshift(value.not_nil!)\n end\nend"}},{"id":"shift-instance-method","html_id":"shift-instance-method","name":"shift","doc":"Returns the first `Node` from the list and removes it.\n\n```\nlist = LinkedList(Float64).new(1.23)\nlist.push(4.56)\nlist.shift() # => 1.23\nlist.peek() # => 4.56\n```","summary":"

Returns the first Node from the list and removes it.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L116","def":{"name":"shift","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"if @head.next.nil?\n return\nend\nfirst = @head.next.not_nil!\n@head.next = first.next\nfirst.value\n"}},{"id":"unshift(value:A)-instance-method","html_id":"unshift(value:A)-instance-method","name":"unshift","doc":"Adds a *value* to the beginning of a linked list.\n\n```\nlist = LinkedList(Int32).new(1)\nlist.unshift(2)\nlist.pop() # => 1\nlist.pop() # => 2\n```","summary":"

Adds a value to the beginning of a linked list.

","abstract":false,"args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"args_string":"(value : A)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L98","def":{"name":"unshift","args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"A"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"new_top = Node(A).new(value)\nif @tail == @head\n @tail = new_top\nend\nnew_top.next = @head.next\n@head.next = new_top\nnew_top.value\n"}}],"macros":[],"types":[{"html_id":"github.com/reduced-reptile/crystal-linked-list/LinkedList/Node","path":"LinkedList/Node.html","kind":"class","full_name":"LinkedList::Node(T)","name":"Node","abstract":false,"superclass":{"html_id":"github.com/reduced-reptile/crystal-linked-list/Reference","kind":"class","full_name":"Reference","name":"Reference"},"ancestors":[{"html_id":"github.com/reduced-reptile/crystal-linked-list/Reference","kind":"class","full_name":"Reference","name":"Reference"},{"html_id":"github.com/reduced-reptile/crystal-linked-list/Object","kind":"class","full_name":"Object","name":"Object"}],"locations":[{"filename":"linked_list.cr","line_number":308,"url":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr"}],"repository_name":"github.com/reduced-reptile/crystal-linked-list","program":false,"enum":false,"alias":false,"aliased":"","const":false,"constants":[],"included_modules":[],"extended_modules":[],"subclasses":[],"including_types":[],"namespace":{"html_id":"github.com/reduced-reptile/crystal-linked-list/LinkedList","kind":"class","full_name":"LinkedList(A)","name":"LinkedList"},"doc":"A node is the building block of linked lists consisting of a values\nand a pointer to the next node in the linked list.\n\nTo create a node:\n\n```\nnode = Node.new(5)\nputs node.value\n```\n\nThe above produces:\n\n```text\n5\n```\n\nCheck the value of the node with `#value`.\nGet the next node in the list with `#next`.","summary":"

A node is the building block of linked lists consisting of a values and a pointer to the next node in the linked list.

","class_methods":[],"constructors":[{"id":"new(value:T)-class-method","html_id":"new(value:T)-class-method","name":"new","doc":"Creates a node with the specified *value*.","summary":"

Creates a node with the specified value.

","abstract":false,"args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"T"}],"args_string":"(value : T)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L317","def":{"name":"new","args":[{"name":"value","doc":null,"default_value":"","external_name":"value","restriction":"T"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"_ = Node(T).allocate\n_.initialize(value)\nif _.responds_to?(:finalize)\n ::GC.add_finalizer(_)\nend\n_\n"}},{"id":"new-class-method","html_id":"new-class-method","name":"new","doc":"Creates a node with no value.","summary":"

Creates a node with no value.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L313","def":{"name":"new","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"_ = Node(T).allocate\n_.initialize\nif _.responds_to?(:finalize)\n ::GC.add_finalizer(_)\nend\n_\n"}}],"instance_methods":[{"id":"next-instance-method","html_id":"next-instance-method","name":"next","doc":"Returns the next node in the linked list, or nil if it is the tail.\n\n```\nnode = Node.new(1)\nnode.next = Node.new(2)\nnode.next.value # => 2\n```","summary":"

Returns the next node in the linked list, or nil if it is the tail.

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L336","def":{"name":"next","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"@next"}},{"id":"next=(next_node:LinkedList::Node(T)?)-instance-method","html_id":"next=(next_node:LinkedList::Node(T)?)-instance-method","name":"next=","doc":"Sets the next node in the linked list to *next_node*\n\n```\nnode = Node.new(1)\nnode.next = Node.new(2)\nnode.next.value # => 2\n```","summary":"

Sets the next node in the linked list to next_node

","abstract":false,"args":[{"name":"next_node","doc":null,"default_value":"","external_name":"next_node","restriction":"Node(T) | Nil"}],"args_string":"(next_node : LinkedList::Node(T)?)","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L347","def":{"name":"next=","args":[{"name":"next_node","doc":null,"default_value":"","external_name":"next_node","restriction":"Node(T) | Nil"}],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"@next = next_node"}},{"id":"value-instance-method","html_id":"value-instance-method","name":"value","doc":"Returns the value of the node, or nil if no value was supplied\n\n```\nNode.new(1).value # => 1\n```","summary":"

Returns the value of the node, or nil if no value was supplied

","abstract":false,"args":[],"args_string":"","source_link":"https://github.com/reduced-reptile/crystal-linked-list/blob/ab6f247b4f63ae12cc36090dcab55597b4d0b88a/src/linked_list.cr#L325","def":{"name":"value","args":[],"double_splat":null,"splat_index":null,"yields":null,"block_arg":null,"return_type":"","visibility":"Public","body":"@value"}}],"macros":[],"types":[]}]}]}}) -------------------------------------------------------------------------------- /shard.lock: -------------------------------------------------------------------------------- 1 | version: 1.0 2 | shards: 3 | ameba: 4 | github: crystal-ameba/ameba 5 | version: 0.11.0 6 | 7 | -------------------------------------------------------------------------------- /shard.yml: -------------------------------------------------------------------------------- 1 | name: linked_list 2 | version: 0.2.0 3 | 4 | crystal: 0.21.1 5 | license: MIT 6 | 7 | authors: 8 | - abvdasker 9 | 10 | development_dependencies: 11 | ameba: 12 | github: crystal-ameba/ameba 13 | version: ~> 0.11.0 14 | 15 | targets: 16 | linked_list: 17 | main: src/linked_list.cr 18 | linked_list_benchmark: 19 | main: src/linked_list_benchmark.cr 20 | 21 | description: Simple linked list implementation -------------------------------------------------------------------------------- /spec/linked_list_spec.cr: -------------------------------------------------------------------------------- 1 | require "./spec_helper" 2 | 3 | describe LinkedList do 4 | describe "#initialize" do 5 | it "initializes the list" do 6 | list = LinkedList(Nil).new 7 | list.size.should eq 0 8 | end 9 | 10 | it "initializes the list with the given values" do 11 | list = LinkedList(Nil | Int32 | String).new(1, 13, "foo") 12 | 13 | list_array = list.to_a 14 | list_array[0].should eq 1 15 | list_array[1].should eq 13 16 | list_array[2].should eq "foo" 17 | end 18 | end 19 | 20 | describe "#size" do 21 | it "returns 0 when the list is newly initialized" do 22 | list = LinkedList(Nil).new 23 | list.size.should eq 0 24 | end 25 | 26 | it "returns 1 when the list is newly initialized with a single value" do 27 | list = LinkedList(Int32).new(1) 28 | list.size.should eq 1 29 | end 30 | end 31 | 32 | describe "#append" do 33 | it "increases the size of the list by 1" do 34 | list = LinkedList(Nil).new(nil) 35 | 36 | list.append(nil) 37 | 38 | list.peek.should eq nil 39 | list.size.should eq 2 40 | end 41 | 42 | it "appends multiple elements to the list" do 43 | list = LinkedList(Int32 | String).new 44 | 45 | list.append(1, "foo") 46 | 47 | list.peek.should eq "foo" 48 | list.size.should eq 2 49 | end 50 | end 51 | 52 | describe "#push" do 53 | it "increases the size of the list by 1" do 54 | list = LinkedList(Nil).new(nil) 55 | 56 | list.push(nil) 57 | 58 | list.peek.should eq nil 59 | list.size.should eq 2 60 | end 61 | 62 | it "adds the element to the end of the list" do 63 | list = LinkedList(Int32).new(1) 64 | list.peek.should eq 1 65 | 66 | list.push(2) 67 | 68 | list.peek.should eq 2 69 | list.size.should eq 2 70 | end 71 | 72 | it "push multiple elements to the list" do 73 | list = LinkedList(Int32).new 74 | list.peek.should eq nil 75 | 76 | list.push(1, 2) 77 | 78 | list.peek.should eq 2 79 | list.size.should eq 2 80 | end 81 | end 82 | 83 | describe "#insert_at" do 84 | it "insert into an empty list at index 0" do 85 | list = LinkedList(Int32).new 86 | list.insert_at(1, 0) 87 | 88 | list.size.should eq 1 89 | 90 | list[0].should eq 1 91 | end 92 | 93 | it "insert fails if not contiguous" do 94 | list = LinkedList(Int32).new 95 | list.insert_at(1, 1) 96 | list.size.should eq 0 97 | end 98 | 99 | it "insert into a populated list at index 0 ( unshift )" do 100 | list = LinkedList(Int32).new(1, 2) 101 | list.insert_at(3, 0) 102 | 103 | list.size.should eq 3 104 | 105 | list[0].should eq 3 106 | list[1].should eq 1 107 | list[2].should eq 2 108 | end 109 | 110 | it "insert into a populated list at index n" do 111 | list = LinkedList(Int32).new(1, 2) 112 | list.insert_at(3, 1) 113 | 114 | list.size.should eq 3 115 | 116 | list[0].should eq 1 117 | list[1].should eq 3 118 | list[2].should eq 2 119 | end 120 | end 121 | 122 | describe "#insert_at" do 123 | it "insert into an empty list at index 0" do 124 | list = LinkedList(Int32).new 125 | list.insert_at(1, 0) 126 | 127 | list.size.should eq 1 128 | 129 | list_a = list.to_a 130 | 131 | list_a[0].should eq 1 132 | end 133 | 134 | it "insert fails if not contiguous" do 135 | list = LinkedList(Int32).new 136 | list.insert_at(1, 1) 137 | list.size.should eq 0 138 | end 139 | 140 | it "insert into a populated list at index 0 ( unshift )" do 141 | list = LinkedList(Int32).new(1, 2) 142 | list.insert_at(3, 0) 143 | 144 | list.size.should eq 3 145 | 146 | list_a = list.to_a 147 | 148 | list_a[0].should eq 3 149 | list_a[1].should eq 1 150 | list_a[2].should eq 2 151 | end 152 | 153 | it "insert into a populated list at index n" do 154 | list = LinkedList(Int32).new(1, 2) 155 | list.insert_at(3, 1) 156 | 157 | list.size.should eq 3 158 | 159 | list_a = list.to_a 160 | 161 | list_a[0].should eq 1 162 | list_a[1].should eq 3 163 | list_a[2].should eq 2 164 | end 165 | end 166 | 167 | describe "#<<" do 168 | it "increases the size of the list by 1" do 169 | list = LinkedList(Nil).new(nil) 170 | list << nil 171 | 172 | list.peek.should eq nil 173 | list.size.should eq 2 174 | end 175 | 176 | it "adds the element to the end of the list" do 177 | list = LinkedList(Int32).new(1) 178 | list << 2 << 3 179 | 180 | list.peek.should eq 3 181 | list.size.should eq 3 182 | end 183 | end 184 | 185 | describe "#peek" do 186 | it "returns nil when list is empty" do 187 | list = LinkedList(Int32).new 188 | list.peek.should eq nil 189 | list.size.should eq 0 190 | end 191 | 192 | it "returns the last element of the list without removing it" do 193 | list = LinkedList(Int32).new(100, 12) 194 | list.peek.should eq 12 195 | list.size.should eq 2 196 | end 197 | end 198 | 199 | describe "#pop" do 200 | it "returns nil when list is empty" do 201 | list = LinkedList(Int32).new 202 | list.pop.should eq nil 203 | list.size.should eq 0 204 | end 205 | 206 | it "returns the last element of the list and removes it" do 207 | list = LinkedList(Int32).new(100, 12) 208 | list.pop.should eq 12 209 | list.peek.should eq 100 210 | list.size.should eq 1 211 | end 212 | 213 | it "returns nil when the list is empty" do 214 | list = LinkedList(Int32).new(100) 215 | list.pop() 216 | list.size.should eq 0 217 | list.pop().should eq nil 218 | end 219 | end 220 | 221 | describe "#empty?" do 222 | it "returns true when there are no elements in the list" do 223 | list = LinkedList(Int32).new 224 | 225 | list.empty?.should be_true 226 | end 227 | 228 | it "returns false" do 229 | list = LinkedList(Int32).new(1, 2) 230 | 231 | list.empty?.should be_false 232 | end 233 | 234 | it "returns true when all elements have been removed from the list" do 235 | list = LinkedList(Int32).new(1, 2) 236 | list.empty?.should be_false 237 | 238 | list.pop() 239 | list.empty?.should be_false 240 | 241 | list.pop() 242 | list.empty?.should be_true 243 | end 244 | end 245 | 246 | describe "#unshift" do 247 | it "prepends an element to the list" do 248 | list = LinkedList(String).new 249 | list.size.should eq 0 250 | list.unshift("foo") 251 | list.size.should eq 1 252 | list.peek.should eq "foo" 253 | end 254 | 255 | it "prepends an element to the list multiple times" do 256 | list = LinkedList(String).new 257 | list.size.should eq 0 258 | list.unshift("foo") 259 | list.size.should eq 1 260 | list.unshift("bar") 261 | list.size.should eq 2 262 | list.peek.should eq "foo" 263 | end 264 | 265 | it "prepends an element to populated list" do 266 | list = LinkedList(String).new("blah") 267 | list.size.should eq 1 268 | list.unshift("foo") 269 | list.size.should eq 2 270 | list.unshift("bar") 271 | list.size.should eq 3 272 | list.peek.should eq "blah" 273 | end 274 | end 275 | 276 | describe "#shift" do 277 | it "returns nil when list is empty" do 278 | list = LinkedList(Float64).new 279 | list.shift.should eq nil 280 | end 281 | 282 | it "returns the first element from the list and removes it" do 283 | list = LinkedList(Float64).new(32.1) 284 | list.shift.should eq 32.1 285 | list.shift.should eq nil 286 | end 287 | 288 | it "returns the first element from the list and removes it, multiple values" do 289 | list = LinkedList(Float64).new(32.1, 64.2) 290 | list.shift.should eq 32.1 291 | list.shift.should eq 64.2 292 | list.shift.should eq nil 293 | end 294 | end 295 | 296 | describe "#index_operator" do 297 | it "get multiple elements via the index" do 298 | list = LinkedList(Int32).new 299 | list.append(1) 300 | list.append(2) 301 | list[0].should eq 1 302 | list[1].should eq 2 303 | end 304 | end 305 | 306 | describe "#to_a" do 307 | it "linked list converted into an array" do 308 | list = LinkedList(Int32).new(1, 2) 309 | 310 | list_a = list.to_a 311 | 312 | list_a[0] = 1 313 | list_a[1] = 2 314 | end 315 | end 316 | 317 | describe "#+" do 318 | it "concatenates two lists and returns a new list" do 319 | first_list = LinkedList(Int32).new(1, 2) 320 | second_list = LinkedList(String).new("foo", "bar") 321 | 322 | result = first_list + second_list 323 | result.size.should eq 4 324 | 325 | result_array = result.to_a 326 | 327 | result_array[0].should eq 1 328 | result_array[1].should eq 2 329 | result_array[-1].should eq "bar" 330 | result_array[-2].should eq "foo" 331 | end 332 | 333 | it "concatenates an array with a list and retuns a new list" do 334 | first_list = LinkedList(Int32).new(1, 2) 335 | array = ["foo", "bar"] 336 | 337 | result = first_list + array 338 | result.size.should eq 4 339 | 340 | result[0].should eq 1 341 | result[1].should eq 2 342 | result[2].should eq "foo" 343 | result[3].should eq "bar" 344 | end 345 | end 346 | 347 | describe "#concat" do 348 | it "concatenates two lists modifying the first list" do 349 | first_list = LinkedList(Int32).new(1, 2) 350 | second_list = LinkedList(Int32).new(3, 4) 351 | 352 | first_list.concat(second_list) 353 | first_list.size.should eq 4 354 | 355 | first_list[0].should eq 1 356 | first_list[1].should eq 2 357 | first_list[2].should eq 3 358 | first_list[3].should eq 4 359 | end 360 | end 361 | 362 | describe "#reverse" do 363 | it "creates a copy of the linked list with the order reversed" do 364 | list = LinkedList(Int32).new(1, 2, 3) 365 | reversed_list = list.reverse 366 | 367 | reversed_list.shift.should eq 3 368 | reversed_list.shift.should eq 2 369 | reversed_list.shift.should eq 1 370 | end 371 | end 372 | 373 | describe "#each" do 374 | it "iterates over each value" do 375 | values = [1, 2, "test"] 376 | list = LinkedList(Int32 | String).new(values) 377 | 378 | list.each do |elem| 379 | values.shift.should eq elem 380 | end 381 | end 382 | 383 | it "iterates over nothing" do 384 | list = LinkedList(Int32 | String).new 385 | 386 | list.each do |_| 387 | false.should be_true 388 | end 389 | end 390 | end 391 | 392 | describe "#to_s" do 393 | it "confirm the string output for a linked list" do 394 | list = LinkedList(Int32 | String).new 395 | list.append(1) 396 | list.append(2) 397 | list.append("test") 398 | list.to_s.should eq "[ 1, 2, test ]" 399 | end 400 | end 401 | 402 | describe "continuously populate and empty" do 403 | # testing that pop sets the list to nil 404 | it "init and pop" do 405 | list = LinkedList(Int32).new(1,2,3) 406 | 407 | list.pop() 408 | list.pop() 409 | list.pop() 410 | 411 | list.append(4) 412 | list.size.should eq 1 413 | 414 | list[0].should eq 4 415 | 416 | end 417 | 418 | it "init and shift" do 419 | list = LinkedList(Int32).new(1,2,3) 420 | 421 | list.shift() 422 | list.shift() 423 | list.shift() 424 | 425 | list.append(4) 426 | list.size.should eq 1 427 | 428 | list[0].should eq 4 429 | 430 | end 431 | 432 | it "init and reverse and shift" do 433 | list = LinkedList(Int32).new(1,2,3) 434 | 435 | reversed_list = list.reverse 436 | 437 | reversed_list.shift() 438 | reversed_list.shift() 439 | reversed_list.shift() 440 | 441 | reversed_list.append(4) 442 | reversed_list.size.should eq 1 443 | 444 | reversed_list[0].should eq 4 445 | end 446 | end 447 | 448 | end 449 | -------------------------------------------------------------------------------- /spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | require "spec" 2 | require "../src/linked_list" 3 | -------------------------------------------------------------------------------- /src/linked_list.cr: -------------------------------------------------------------------------------- 1 | # A linked list is a **Enumerable** (see the `Enumerable` module) data structure 2 | # that stores multiple pieces of data in non contiguous locations in memory. 3 | # 4 | # To create a linked list: 5 | # 6 | # ``` 7 | # list = LinkedList(Int32).new 8 | # list.push(2) 9 | # puts list.pop 10 | # ``` 11 | # 12 | # The above produces: 13 | # 14 | # ```text 15 | # 2 16 | # ``` 17 | class LinkedList(A) 18 | @head : Node(A) | Nil 19 | @tail : Node(A) | Nil 20 | 21 | include Enumerable(A | Nil) 22 | 23 | # Creates an empty linked list. 24 | def initialize 25 | @head = nil 26 | @tail = @head 27 | end 28 | 29 | # Creates a linked list with the *values* as the values of the nodes. 30 | def initialize(*values) 31 | initialize(values) 32 | end 33 | 34 | # ditto 35 | def initialize(values : Enumerable(A)) 36 | @head = nil 37 | @tail = @head 38 | values.each do |value| 39 | append(value) 40 | end 41 | end 42 | 43 | # Adds a *value* to the end of a linked list. 44 | # 45 | # ``` 46 | # list = LinkedList(Int32).new 47 | # list.append(1) 48 | # list.push(2) 49 | # list.pop() # => 2 50 | # list.pop() # => 1 51 | # ``` 52 | def append(value : A) 53 | if @head.nil? 54 | @head = Node(A).new(value) 55 | @tail = @head 56 | else 57 | new_node = Node(A).new(value) 58 | @tail.not_nil!.next = new_node 59 | @tail = new_node 60 | new_node.value 61 | end 62 | end 63 | 64 | # ditto 65 | def push(value : A) 66 | append(value) 67 | end 68 | 69 | # Adds a *value* to the linked list at a specified index. 70 | # 71 | # ``` 72 | # list = LinkedList(Int32).new 73 | # list.append(1) 74 | # list.append(2) 75 | # list.insert_at(3, 1) 76 | # ``` 77 | def insert_at(value : A, index : Int32) 78 | # non-contiguous 79 | return nil if index > size 80 | 81 | # inserting at the beginning is the unshift function 82 | if index == 0 83 | unshift(value) 84 | return 85 | end 86 | 87 | # not index 0, but list is empty 88 | # non-contiguous 89 | if @head.nil? 90 | return nil 91 | end 92 | 93 | # start at head and move to the index 94 | last = @tail 95 | previous = @head 96 | current = @head.not_nil!.next.not_nil! 97 | 98 | search = 1 99 | 100 | while current != last && search < index 101 | previous = current 102 | current = current.next.not_nil! 103 | search += 1 104 | end 105 | 106 | # made it to the desired index 107 | if search != index 108 | return nil 109 | end 110 | 111 | new_node = Node(A).new(value) 112 | 113 | previous.not_nil!.next = new_node 114 | new_node.next = current 115 | end 116 | 117 | # Override the << (shift) operator to add a *value* to the end of a 118 | # linked list. This method returns itself so it can be chained. 119 | # 120 | # ``` 121 | # list = LinkedList(Int32).new(1) 122 | # list << 2 << 3 123 | # list.pop() # => 3 124 | # list.pop() # => 2 125 | # list.pop() # => 1 126 | # ``` 127 | def <<(value : A) 128 | append(value) 129 | self 130 | end 131 | 132 | # Adds a list of *values* to the end of a linked list. 133 | # 134 | # ``` 135 | # list = LinkedList(Int32 | String).new 136 | # list.append(1, "foo") 137 | # list.pop() # => "foo" 138 | # list.pop() # => 1 139 | # ``` 140 | def append(*values) 141 | values.each do |value| 142 | append(value) 143 | end 144 | end 145 | 146 | # same as append 147 | def push(*values) 148 | values.each do |value| 149 | push(value) 150 | end 151 | end 152 | 153 | # Adds a *value* to the beginning of a linked list. 154 | # 155 | # ``` 156 | # list = LinkedList(Int32).new(1) 157 | # list.unshift(2) 158 | # list.pop() # => 1 159 | # list.pop() # => 2 160 | # ``` 161 | def unshift(value : A) 162 | if @head.nil? 163 | append(value) 164 | return 165 | end 166 | new_top = Node(A).new(value) 167 | new_top.next = @head 168 | @head = new_top 169 | new_top.value 170 | end 171 | 172 | # Returns the first `Node` from the list and removes it. 173 | # 174 | # ``` 175 | # list = LinkedList(Float64).new(1.23) 176 | # list.push(4.56) 177 | # list.shift() # => 1.23 178 | # list.peek() # => 4.56 179 | # ``` 180 | def shift 181 | return if @head.nil? 182 | 183 | first = @head.not_nil! 184 | @head = head.not_nil!.next 185 | first.value 186 | end 187 | 188 | # Returns the value of the tail of the linked list, 189 | # or nil if no value was supplied. 190 | # 191 | # ``` 192 | # list = LinkedList(Float64).new(1.23) 193 | # list.push(4.56) 194 | # list.peek() # => 4.56 195 | # ``` 196 | def peek 197 | return nil if @tail.nil? 198 | @tail.not_nil!.value 199 | end 200 | 201 | # Returns the last `Node` from the list and removes it. 202 | # 203 | # ``` 204 | # list = LinkedList(Float64).new(1.23) 205 | # list.push(4.56) 206 | # list.pop() # => 4.56 207 | # list.peek() # => 1.23 208 | # ``` 209 | def pop 210 | return nil if @head.nil? 211 | 212 | if @head == @tail 213 | current = @head 214 | @head = nil 215 | @tail = nil 216 | return current.not_nil!.value 217 | end 218 | 219 | last = @tail 220 | current = @head 221 | while current.not_nil!.next != last 222 | current = current.not_nil!.next.not_nil! 223 | end 224 | 225 | current.not_nil!.next = nil 226 | @tail = current 227 | last.not_nil!.value 228 | end 229 | 230 | # Iterates over all the values in the linked list. 231 | # 232 | # ``` 233 | # values = [1, 2, 3] 234 | # list = LinkedList(Int32).new(values) 235 | # list.each do |elem| 236 | # puts elem 237 | # end 238 | # 239 | # The above produces: 240 | # 241 | # ```text 242 | # 1 243 | # 2 244 | # 3 245 | # ``` 246 | def each 247 | each_node do |node| 248 | yield node.value 249 | end 250 | self 251 | end 252 | 253 | # Returns a new `LinkedList` with all of the elements from the first list 254 | # followed by all of the elements in the second *list*. 255 | # 256 | # ``` 257 | # first_list = LinkedList(Int32).new(1, 2) 258 | # second_list = LinkedList(String).new("foo", "bar") 259 | # combined_list = first_list + second_list 260 | # combined_list.peek() # => "bar" 261 | # combined_list.shift() # => 1 262 | # ``` 263 | def +(list : Enumerable(C)) forall C 264 | LinkedList(A | C).new.tap do |new_list| 265 | each do |value| 266 | new_list.append(value) 267 | end 268 | list.each do |value| 269 | new_list.append(value) 270 | end 271 | end 272 | end 273 | 274 | def [](index : Int32) 275 | return nil if @head.nil? 276 | 277 | search = 0 278 | each_node do |node| 279 | if search == index 280 | return node.value 281 | end 282 | 283 | search += 1 284 | end 285 | end 286 | 287 | # Adds all the elemenets of the *list* to the end of the current linked list. 288 | # 289 | # ``` 290 | # first_list = LinkedList(Int32).new(1, 2) 291 | # second_list = LinkedList(Int32).new(3, 4) 292 | # combined_list = first_list.concat(second_list) 293 | # combined_list.peek() # => 4 294 | # combined_list.shift() # => 1 295 | # ``` 296 | def concat(list : LinkedList(A)) 297 | @tail.not_nil!.next = list.head 298 | @tail = list.tail 299 | self 300 | end 301 | 302 | # Returns true if and only if there are no elements in the list. 303 | # 304 | # ``` 305 | # list = LinkedList(Int32).new 306 | # list.empty? # => true 307 | # list.push(1) 308 | # list.empty? # => false 309 | # ``` 310 | def empty? 311 | @head.nil? 312 | end 313 | 314 | # Creates a copy of the `LinkedList` with the order reversed. 315 | # 316 | # ``` 317 | # list = LinkedList(Int32).new(1, 2, 3) 318 | # reversed_list = list.reverse 319 | # list.pop() # => 1 320 | # list.pop() # => 2 321 | # list.pop() # => 3 322 | # ``` 323 | def reverse 324 | LinkedList(A).new.tap do |new_list| 325 | each do |value| 326 | new_list.unshift(value.not_nil!) 327 | end 328 | end 329 | end 330 | 331 | # Returns the first node in the list, or nil if the list is empty. 332 | # 333 | # ``` 334 | # list = LinkedList(Int32).new(1, 2, 3) 335 | # list.head.value # => 1 336 | # ``` 337 | protected def head 338 | @head 339 | end 340 | 341 | # Returns the last node in the list, or nil if the list is empty. 342 | # 343 | # ``` 344 | # list = LinkedList(Int32).new(1, 2, 3) 345 | # list.tail.value # => 3 346 | # ``` 347 | protected def tail 348 | @tail 349 | end 350 | 351 | # Iterates over all the nodes in the linked list. 352 | # 353 | # ``` 354 | # values = [1, 2, 3] 355 | # list = LinkedList(Int32).new(values) 356 | # list.each_node do |elem| 357 | # puts elem.value 358 | # end 359 | # 360 | # The above produces: 361 | # 362 | # ```text 363 | # 1 364 | # 2 365 | # 3 366 | # ``` 367 | private def each_node 368 | return nil if @head.nil? 369 | 370 | current = @head 371 | loop do 372 | yield current.not_nil! 373 | current = current.not_nil!.next 374 | break if current.nil? 375 | end 376 | end 377 | 378 | # Overloading the to_s function to print the contents 379 | # of the linked list 380 | # This calls the to_s function of each node in the 381 | # linked list 382 | # 383 | # ``` 384 | # values = [1, 2, 3] 385 | # puts values 386 | # 387 | # The above produces: 388 | # 389 | # ```text 390 | # [ 1, 2, 3 ] 391 | # ``` 392 | def to_s(io) 393 | io << "[ " 394 | 395 | # iterate through the nodes in the linked list 396 | each_node do |elem| 397 | io << elem.value 398 | # kind of clunky, if this is the tail node 399 | # don't print the comma 400 | if elem != @tail 401 | io << ", " 402 | end 403 | end 404 | 405 | io << " ]" 406 | end 407 | 408 | # A node is the building block of linked lists consisting of a values 409 | # and a pointer to the next node in the linked list. 410 | # 411 | # To create a node: 412 | # 413 | # ``` 414 | # node = Node.new(5) 415 | # puts node.value 416 | # ``` 417 | # 418 | # The above produces: 419 | # 420 | # ```text 421 | # 5 422 | # ``` 423 | # 424 | # Check the value of the node with `#value`. 425 | # Get the next node in the list with `#next`. 426 | class Node(T) 427 | @next = nil 428 | @value = nil 429 | 430 | # Creates a node with no value. 431 | def initialize 432 | end 433 | 434 | # Creates a node with the specified *value*. 435 | def initialize(@value : T) 436 | end 437 | 438 | # Returns the value of the node, or nil if no value was supplied 439 | # 440 | # ``` 441 | # Node.new(1).value # => 1 442 | # ``` 443 | def value 444 | @value 445 | end 446 | 447 | # Returns the next node in the linked list, or nil if it is the tail. 448 | # 449 | # ``` 450 | # node = Node.new(1) 451 | # node.next = Node.new(2) 452 | # node.next.value # => 2 453 | # ``` 454 | def next 455 | @next 456 | end 457 | 458 | # Sets the next node in the linked list to *next_node* 459 | # 460 | # ``` 461 | # node = Node.new(1) 462 | # node.next = Node.new(2) 463 | # node.next.value # => 2 464 | # ``` 465 | def next=(next_node : Node(T) | Nil) 466 | @next = next_node 467 | end 468 | 469 | # Overloading the to_s function to print the contents 470 | # of the node 471 | def to_s(io) 472 | io << @value 473 | end 474 | end 475 | end 476 | -------------------------------------------------------------------------------- /src/linked_list_benchmark.cr: -------------------------------------------------------------------------------- 1 | # TODO: Write documentation for `Benchmark` 2 | 3 | require "./linked_list" 4 | require "benchmark" 5 | 6 | module LinkedListBenchmark 7 | VERSION = "0.1.0" 8 | 9 | # TODO: Put your code here 10 | 11 | # ======================================== 12 | # benchmark appending 13 | list1 = LinkedList(Int32).new 14 | list2 = LinkedList(Int32).new 15 | list3 = LinkedList(Int32).new 16 | # Benchmark.ips do |x| 17 | # x.report("append") { 1000.times{ list.append(1) } } 18 | # end 19 | 20 | puts Benchmark.measure { 100.times{ list1.append(1) } } 21 | puts Benchmark.measure { 10_000.times{ list2.append(1) } } 22 | puts Benchmark.measure { 1_000_000.times{ list3.append(1) } } 23 | 24 | puts Benchmark.measure { list1.insert_at(1, 1) } 25 | puts Benchmark.measure { list2.insert_at(1, 1) } 26 | puts Benchmark.measure { list3.insert_at(1, 1) } 27 | # ======================================== 28 | 29 | end --------------------------------------------------------------------------------