hamster 1.0.0 → 1.0.1.pre.rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/hamster.rb +2 -3
- data/lib/hamster/core_ext.rb +1 -0
- data/lib/hamster/core_ext/enumerable.rb +17 -17
- data/lib/hamster/core_ext/enumerator.rb +16 -0
- data/lib/hamster/core_ext/io.rb +17 -15
- data/lib/hamster/enumerable.rb +107 -123
- data/lib/hamster/experimental/mutable_queue.rb +6 -2
- data/lib/hamster/experimental/mutable_set.rb +3 -1
- data/lib/hamster/experimental/mutable_stack.rb +30 -0
- data/lib/hamster/hash.rb +95 -713
- data/lib/hamster/immutable.rb +0 -4
- data/lib/hamster/list.rb +242 -1062
- data/lib/hamster/mutable_hash.rb +3 -1
- data/lib/hamster/queue.rb +87 -0
- data/lib/hamster/read_copy_update.rb +1 -2
- data/lib/hamster/set.rb +73 -478
- data/lib/hamster/sorter.rb +25 -0
- data/lib/hamster/stack.rb +75 -0
- data/lib/hamster/trie.rb +48 -199
- data/lib/hamster/tuple.rb +39 -0
- data/lib/hamster/undefined.rb +3 -1
- data/lib/hamster/vector.rb +72 -1326
- data/lib/hamster/version.rb +1 -1
- data/spec/hamster/core_ext/array_spec.rb +20 -0
- data/spec/{lib/hamster → hamster}/core_ext/enumerable_spec.rb +0 -5
- data/spec/hamster/core_ext/enumerator_spec.rb +19 -0
- data/spec/{lib/hamster → hamster}/core_ext/io_spec.rb +0 -0
- data/spec/hamster/experimental/mutable_set/add_qm_spec.rb +47 -0
- data/spec/hamster/experimental/mutable_set/add_spec.rb +51 -0
- data/spec/hamster/experimental/mutable_set/delete_qm_spec.rb +47 -0
- data/spec/hamster/experimental/mutable_set/delete_spec.rb +47 -0
- data/spec/hamster/experimental/mutable_stack/pop_spec.rb +41 -0
- data/spec/hamster/experimental/mutable_stack/push_spec.rb +41 -0
- data/spec/hamster/hash/all_spec.rb +59 -0
- data/spec/hamster/hash/any_spec.rb +67 -0
- data/spec/hamster/hash/clear_spec.rb +36 -0
- data/spec/hamster/hash/construction_spec.rb +35 -0
- data/spec/{lib/hamster → hamster}/hash/copying_spec.rb +12 -3
- data/spec/hamster/hash/delete_spec.rb +47 -0
- data/spec/hamster/hash/each_spec.rb +41 -0
- data/spec/hamster/hash/empty_spec.rb +27 -0
- data/spec/hamster/hash/eql_spec.rb +62 -0
- data/spec/hamster/hash/except_spec.rb +31 -0
- data/spec/hamster/hash/fetch_spec.rb +95 -0
- data/spec/hamster/hash/filter_spec.rb +63 -0
- data/spec/hamster/hash/find_spec.rb +58 -0
- data/spec/hamster/hash/get_spec.rb +55 -0
- data/spec/hamster/hash/has_key_spec.rb +35 -0
- data/spec/hamster/hash/hash_spec.rb +52 -0
- data/spec/{lib/hamster → hamster}/hash/immutable_spec.rb +3 -0
- data/spec/hamster/hash/inspect_spec.rb +32 -0
- data/spec/hamster/hash/keys_spec.rb +21 -0
- data/spec/hamster/hash/map_spec.rb +64 -0
- data/spec/{lib/hamster → hamster}/hash/marshal_spec.rb +3 -3
- data/spec/hamster/hash/merge_spec.rb +36 -0
- data/spec/{lib/hamster → hamster}/hash/none_spec.rb +14 -12
- data/spec/hamster/hash/put_spec.rb +65 -0
- data/spec/hamster/hash/reduce_spec.rb +60 -0
- data/spec/hamster/hash/remove_spec.rb +63 -0
- data/spec/{lib/hamster → hamster}/hash/size_spec.rb +2 -2
- data/spec/hamster/hash/slice_spec.rb +26 -0
- data/spec/hamster/hash/uniq_spec.rb +23 -0
- data/spec/hamster/hash/values_spec.rb +34 -0
- data/spec/{lib/hamster → hamster}/immutable/copying_spec.rb +8 -1
- data/spec/{lib/hamster → hamster}/immutable/immutable_spec.rb +14 -1
- data/spec/{lib/hamster → hamster}/immutable/memoize_spec.rb +3 -2
- data/spec/{lib/hamster → hamster}/immutable/new_spec.rb +0 -0
- data/spec/{lib/hamster → hamster}/immutable/transform_spec.rb +0 -0
- data/spec/{lib/hamster → hamster}/immutable/transform_unless_spec.rb +0 -0
- data/spec/hamster/list/add_spec.rb +16 -0
- data/spec/hamster/list/all_spec.rb +111 -0
- data/spec/hamster/list/any_spec.rb +79 -0
- data/spec/{lib/hamster → hamster}/list/append_spec.rb +22 -11
- data/spec/hamster/list/at_spec.rb +49 -0
- data/spec/hamster/list/break_spec.rb +85 -0
- data/spec/{lib/hamster → hamster}/list/cadr_spec.rb +17 -6
- data/spec/{lib/hamster → hamster}/list/chunk_spec.rb +17 -6
- data/spec/{lib/hamster → hamster}/list/clear_spec.rb +16 -5
- data/spec/hamster/list/combinations_spec.rb +51 -0
- data/spec/{lib/hamster → hamster}/list/compact_spec.rb +17 -6
- data/spec/hamster/list/cons_spec.rb +41 -0
- data/spec/hamster/list/construction_spec.rb +166 -0
- data/spec/{lib/hamster → hamster}/list/copying_spec.rb +16 -4
- data/spec/hamster/list/count_spec.rb +66 -0
- data/spec/hamster/list/cycle_spec.rb +45 -0
- data/spec/{lib/hamster → hamster}/list/drop_spec.rb +17 -6
- data/spec/hamster/list/drop_while_spec.rb +59 -0
- data/spec/hamster/list/each_slice_spec.rb +80 -0
- data/spec/hamster/list/each_spec.rb +72 -0
- data/spec/hamster/list/each_with_index_spec.rb +42 -0
- data/spec/hamster/list/elem_index_spec.rb +58 -0
- data/spec/hamster/list/elem_indices_spec.rb +45 -0
- data/spec/hamster/list/empty_spec.rb +47 -0
- data/spec/hamster/list/eql_spec.rb +78 -0
- data/spec/hamster/list/filter_spec.rb +70 -0
- data/spec/{lib/hamster → hamster}/list/find_all_spec.rb +2 -3
- data/spec/{lib/hamster → hamster}/list/find_index_spec.rb +27 -5
- data/spec/hamster/list/find_indices_spec.rb +45 -0
- data/spec/{lib/hamster → hamster}/list/find_spec.rb +33 -11
- data/spec/{lib/hamster → hamster}/list/flat_map_spec.rb +0 -0
- data/spec/{lib/hamster → hamster}/list/flatten_spec.rb +17 -6
- data/spec/{lib/hamster → hamster}/list/grep_spec.rb +33 -10
- data/spec/{lib/hamster → hamster}/list/group_by_spec.rb +44 -9
- data/spec/{lib/hamster → hamster}/list/hash_spec.rb +12 -4
- data/spec/{lib/hamster → hamster}/list/head_spec.rb +18 -3
- data/spec/{lib/hamster → hamster}/list/include_spec.rb +27 -6
- data/spec/{lib/hamster → hamster}/list/init_spec.rb +17 -6
- data/spec/hamster/list/inits_spec.rb +42 -0
- data/spec/hamster/list/inspect_spec.rb +43 -0
- data/spec/{lib/hamster → hamster}/list/intersperse_spec.rb +17 -6
- data/spec/hamster/list/join_spec.rb +81 -0
- data/spec/hamster/list/last_spec.rb +44 -0
- data/spec/{lib/hamster → hamster}/list/map_spec.rb +35 -12
- data/spec/hamster/list/maximum_spec.rb +77 -0
- data/spec/{lib/hamster → hamster}/list/merge_by_spec.rb +32 -5
- data/spec/{lib/hamster → hamster}/list/merge_spec.rb +20 -1
- data/spec/hamster/list/minimum_spec.rb +77 -0
- data/spec/{lib/hamster → hamster}/list/none_spec.rb +39 -12
- data/spec/{lib/hamster → hamster}/list/one_spec.rb +38 -13
- data/spec/hamster/list/partition_spec.rb +75 -0
- data/spec/{lib/hamster → hamster}/list/pop_spec.rb +1 -1
- data/spec/hamster/list/product_spec.rb +44 -0
- data/spec/hamster/list/reduce_spec.rb +73 -0
- data/spec/{lib/hamster/list/reject_spec.rb → hamster/list/remove_spec.rb} +35 -11
- data/spec/{lib/hamster → hamster}/list/reverse_spec.rb +25 -8
- data/spec/{lib/hamster → hamster}/list/select_spec.rb +2 -3
- data/spec/{lib/hamster → hamster}/list/size_spec.rb +26 -5
- data/spec/hamster/list/slice_spec.rb +50 -0
- data/spec/{lib/hamster → hamster}/list/sorting_spec.rb +34 -11
- data/spec/hamster/list/span_spec.rb +86 -0
- data/spec/{lib/hamster → hamster}/list/split_at_spec.rb +23 -14
- data/spec/hamster/list/sum_spec.rb +44 -0
- data/spec/hamster/list/tail_spec.rb +48 -0
- data/spec/hamster/list/tails_spec.rb +42 -0
- data/spec/{lib/hamster → hamster}/list/take_spec.rb +17 -6
- data/spec/{lib/hamster → hamster}/list/take_while_spec.rb +18 -11
- data/spec/hamster/list/to_a_spec.rb +54 -0
- data/spec/{lib/hamster → hamster}/list/to_ary_spec.rb +4 -1
- data/spec/{lib/hamster → hamster}/list/to_list_spec.rb +16 -4
- data/spec/hamster/list/to_set_spec.rb +33 -0
- data/spec/{lib/hamster → hamster}/list/union_spec.rb +23 -6
- data/spec/hamster/list/uniq_spec.rb +45 -0
- data/spec/{lib/hamster → hamster}/list/zip_spec.rb +9 -2
- data/spec/hamster/queue/clear_spec.rb +36 -0
- data/spec/hamster/queue/construction_spec.rb +43 -0
- data/spec/hamster/queue/dequeue_spec.rb +40 -0
- data/spec/hamster/queue/empty_spec.rb +47 -0
- data/spec/{lib/hamster/deque → hamster/queue}/enqueue_spec.rb +21 -8
- data/spec/hamster/queue/head_spec.rb +35 -0
- data/spec/hamster/queue/inspect_spec.rb +31 -0
- data/spec/{lib/hamster/deque → hamster/queue}/size_spec.rb +20 -5
- data/spec/hamster/queue/to_a_spec.rb +42 -0
- data/spec/{lib/hamster/deque → hamster/queue}/to_ary_spec.rb +6 -6
- data/spec/hamster/queue/to_list_spec.rb +44 -0
- data/spec/hamster/set/add_spec.rb +51 -0
- data/spec/hamster/set/all_spec.rb +67 -0
- data/spec/hamster/set/any_spec.rb +67 -0
- data/spec/hamster/set/clear_spec.rb +36 -0
- data/spec/{lib/hamster → hamster}/set/compact_spec.rb +16 -5
- data/spec/{lib/hamster → hamster}/set/construction_spec.rb +17 -5
- data/spec/{lib/hamster → hamster}/set/copying_spec.rb +12 -3
- data/spec/{lib/hamster → hamster}/set/count_spec.rb +28 -11
- data/spec/hamster/set/delete_spec.rb +47 -0
- data/spec/{lib/hamster/sorted_set → hamster/set}/difference_spec.rb +21 -7
- data/spec/{lib/hamster/set/reverse_each_spec.rb → hamster/set/each_spec.rb} +7 -8
- data/spec/hamster/set/empty_spec.rb +35 -0
- data/spec/{lib/hamster → hamster}/set/eqeq_spec.rb +0 -0
- data/spec/{lib/hamster → hamster}/set/eql_spec.rb +1 -7
- data/spec/{lib/hamster/sorted_set → hamster/set}/exclusion_spec.rb +20 -5
- data/spec/hamster/set/filter_spec.rb +79 -0
- data/spec/{lib/hamster → hamster}/set/find_spec.rb +25 -8
- data/spec/hamster/set/flatten_spec.rb +49 -0
- data/spec/hamster/set/foreach_spec.rb +39 -0
- data/spec/{lib/hamster → hamster}/set/grep_spec.rb +1 -1
- data/spec/hamster/set/group_by_spec.rb +65 -0
- data/spec/hamster/set/hash_spec.rb +20 -0
- data/spec/hamster/set/head_spec.rb +39 -0
- data/spec/{lib/hamster → hamster}/set/immutable_spec.rb +4 -1
- data/spec/hamster/set/include_spec.rb +35 -0
- data/spec/hamster/set/inspect_spec.rb +32 -0
- data/spec/hamster/set/intersection_spec.rb +46 -0
- data/spec/{lib/hamster/vector → hamster/set}/join_spec.rb +33 -23
- data/spec/hamster/set/map_spec.rb +64 -0
- data/spec/{lib/hamster → hamster}/set/marshal_spec.rb +9 -4
- data/spec/hamster/set/maximum_spec.rb +65 -0
- data/spec/hamster/set/minimum_spec.rb +65 -0
- data/spec/{lib/hamster → hamster}/set/none_spec.rb +30 -15
- data/spec/{lib/hamster → hamster}/set/one_spec.rb +29 -14
- data/spec/hamster/set/partition_spec.rb +71 -0
- data/spec/{lib/hamster → hamster}/set/product_spec.rb +15 -7
- data/spec/hamster/set/reduce_spec.rb +87 -0
- data/spec/hamster/set/remove_spec.rb +63 -0
- data/spec/{lib/hamster → hamster}/set/size_spec.rb +10 -1
- data/spec/{lib/hamster → hamster}/set/sorting_spec.rb +16 -18
- data/spec/hamster/set/subset_spec.rb +39 -0
- data/spec/{lib/hamster/vector → hamster/set}/sum_spec.rb +18 -4
- data/spec/hamster/set/superset_spec.rb +39 -0
- data/spec/hamster/set/to_a_spec.rb +42 -0
- data/spec/{lib/hamster/vector → hamster/set}/to_list_spec.rb +23 -8
- data/spec/{lib/hamster → hamster}/set/to_set_spec.rb +15 -3
- data/spec/hamster/set/union_spec.rb +45 -0
- data/spec/hamster/set/uniq_spec.rb +23 -0
- data/spec/hamster/sorter/immutable_spec.rb +12 -0
- data/spec/hamster/stack/clear_spec.rb +36 -0
- data/spec/hamster/stack/construction_spec.rb +43 -0
- data/spec/hamster/stack/copying_spec.rb +31 -0
- data/spec/hamster/stack/empty_spec.rb +31 -0
- data/spec/hamster/stack/eql_spec.rb +60 -0
- data/spec/hamster/stack/immutable_spec.rb +12 -0
- data/spec/hamster/stack/inspect_spec.rb +31 -0
- data/spec/hamster/stack/peek_spec.rb +40 -0
- data/spec/hamster/stack/pop_spec.rb +41 -0
- data/spec/hamster/stack/push_spec.rb +41 -0
- data/spec/hamster/stack/size_spec.rb +35 -0
- data/spec/hamster/stack/to_a_spec.rb +42 -0
- data/spec/hamster/stack/to_ary.rb +37 -0
- data/spec/hamster/stack/to_list_spec.rb +33 -0
- data/spec/hamster/trie/remove_spec.rb +117 -0
- data/spec/hamster/tuple/copying_spec.rb +24 -0
- data/spec/hamster/tuple/eql_spec.rb +61 -0
- data/spec/hamster/tuple/first_spec.rb +19 -0
- data/spec/hamster/tuple/immutable_spec.rb +12 -0
- data/spec/hamster/tuple/inspect_spec.rb +19 -0
- data/spec/hamster/tuple/last_spec.rb +19 -0
- data/spec/hamster/tuple/to_a_spec.rb +38 -0
- data/spec/hamster/tuple/to_ary_spec.rb +38 -0
- data/spec/hamster/undefined/erase_spec.rb +47 -0
- data/spec/hamster/vector/add_spec.rb +48 -0
- data/spec/{lib/hamster → hamster}/vector/any_spec.rb +0 -0
- data/spec/hamster/vector/clear_spec.rb +35 -0
- data/spec/{lib/hamster → hamster}/vector/copying_spec.rb +14 -4
- data/spec/hamster/vector/each_spec.rb +45 -0
- data/spec/hamster/vector/each_with_index_spec.rb +41 -0
- data/spec/hamster/vector/empty_spec.rb +34 -0
- data/spec/hamster/vector/eql_spec.rb +64 -0
- data/spec/hamster/vector/filter_spec.rb +62 -0
- data/spec/hamster/vector/first_spec.rb +35 -0
- data/spec/hamster/vector/get_spec.rb +80 -0
- data/spec/{lib/hamster → hamster}/vector/include_spec.rb +17 -4
- data/spec/hamster/vector/inspect_spec.rb +33 -0
- data/spec/{lib/hamster → hamster}/vector/last_spec.rb +3 -12
- data/spec/{lib/hamster → hamster}/vector/length_spec.rb +3 -12
- data/spec/hamster/vector/map_spec.rb +63 -0
- data/spec/{lib/hamster → hamster}/vector/reduce_spec.rb +47 -16
- data/spec/hamster/vector/set_spec.rb +113 -0
- data/spec/{lib/hamster → hamster}/vector/to_a_spec.rb +12 -11
- data/spec/{lib/hamster → hamster}/vector/to_ary_spec.rb +0 -0
- data/spec/lib/hamster/vector/cons_spec.rb +48 -0
- data/spec/lib/hamster/vector/exist_spec.rb +70 -0
- data/spec/lib/hamster/vector/exists_spec.rb +70 -0
- data/spec/lib/hamster/vector/ltlt_spec.rb +2 -20
- data/spec/spec_helper.rb +5 -36
- metadata +510 -723
- data/lib/hamster/deque.rb +0 -252
- data/lib/hamster/nested.rb +0 -36
- data/lib/hamster/sorted_set.rb +0 -1397
- data/spec/lib/hamster/core_ext/array_spec.rb +0 -14
- data/spec/lib/hamster/deque/clear_spec.rb +0 -34
- data/spec/lib/hamster/deque/construction_spec.rb +0 -30
- data/spec/lib/hamster/deque/copying_spec.rb +0 -20
- data/spec/lib/hamster/deque/dequeue_spec.rb +0 -35
- data/spec/lib/hamster/deque/empty_spec.rb +0 -40
- data/spec/lib/hamster/deque/first_spec.rb +0 -18
- data/spec/lib/hamster/deque/inspect_spec.rb +0 -24
- data/spec/lib/hamster/deque/last_spec.rb +0 -18
- data/spec/lib/hamster/deque/marshal_spec.rb +0 -34
- data/spec/lib/hamster/deque/new_spec.rb +0 -44
- data/spec/lib/hamster/deque/pop_spec.rb +0 -33
- data/spec/lib/hamster/deque/pretty_print_spec.rb +0 -24
- data/spec/lib/hamster/deque/random_modification_spec.rb +0 -34
- data/spec/lib/hamster/deque/to_a_spec.rb +0 -27
- data/spec/lib/hamster/deque/to_list_spec.rb +0 -26
- data/spec/lib/hamster/deque/unshift_spec.rb +0 -26
- data/spec/lib/hamster/experimental/mutable_set/add_qm_spec.rb +0 -39
- data/spec/lib/hamster/experimental/mutable_set/add_spec.rb +0 -37
- data/spec/lib/hamster/experimental/mutable_set/delete_qm_spec.rb +0 -38
- data/spec/lib/hamster/experimental/mutable_set/delete_spec.rb +0 -37
- data/spec/lib/hamster/hash/all_spec.rb +0 -54
- data/spec/lib/hamster/hash/any_spec.rb +0 -54
- data/spec/lib/hamster/hash/assoc_spec.rb +0 -52
- data/spec/lib/hamster/hash/clear_spec.rb +0 -43
- data/spec/lib/hamster/hash/construction_spec.rb +0 -39
- data/spec/lib/hamster/hash/default_proc_spec.rb +0 -73
- data/spec/lib/hamster/hash/delete_spec.rb +0 -40
- data/spec/lib/hamster/hash/each_spec.rb +0 -78
- data/spec/lib/hamster/hash/each_with_index_spec.rb +0 -30
- data/spec/lib/hamster/hash/empty_spec.rb +0 -44
- data/spec/lib/hamster/hash/eql_spec.rb +0 -70
- data/spec/lib/hamster/hash/except_spec.rb +0 -43
- data/spec/lib/hamster/hash/fetch_spec.rb +0 -58
- data/spec/lib/hamster/hash/find_spec.rb +0 -44
- data/spec/lib/hamster/hash/flat_map_spec.rb +0 -36
- data/spec/lib/hamster/hash/flatten_spec.rb +0 -99
- data/spec/lib/hamster/hash/get_spec.rb +0 -80
- data/spec/lib/hamster/hash/has_key_spec.rb +0 -32
- data/spec/lib/hamster/hash/has_value_spec.rb +0 -28
- data/spec/lib/hamster/hash/hash_spec.rb +0 -30
- data/spec/lib/hamster/hash/inspect_spec.rb +0 -31
- data/spec/lib/hamster/hash/invert_spec.rb +0 -31
- data/spec/lib/hamster/hash/key_spec.rb +0 -28
- data/spec/lib/hamster/hash/keys_spec.rb +0 -17
- data/spec/lib/hamster/hash/map_spec.rb +0 -46
- data/spec/lib/hamster/hash/merge_spec.rb +0 -83
- data/spec/lib/hamster/hash/min_max_spec.rb +0 -46
- data/spec/lib/hamster/hash/new_spec.rb +0 -71
- data/spec/lib/hamster/hash/partition_spec.rb +0 -36
- data/spec/lib/hamster/hash/pretty_print_spec.rb +0 -35
- data/spec/lib/hamster/hash/put_spec.rb +0 -103
- data/spec/lib/hamster/hash/reduce_spec.rb +0 -36
- data/spec/lib/hamster/hash/reject_spec.rb +0 -62
- data/spec/lib/hamster/hash/reverse_each_spec.rb +0 -28
- data/spec/lib/hamster/hash/sample_spec.rb +0 -14
- data/spec/lib/hamster/hash/select_spec.rb +0 -58
- data/spec/lib/hamster/hash/slice_spec.rb +0 -45
- data/spec/lib/hamster/hash/sort_spec.rb +0 -27
- data/spec/lib/hamster/hash/store_spec.rb +0 -76
- data/spec/lib/hamster/hash/take_spec.rb +0 -36
- data/spec/lib/hamster/hash/to_a_spec.rb +0 -14
- data/spec/lib/hamster/hash/to_hash_spec.rb +0 -22
- data/spec/lib/hamster/hash/update_in_spec.rb +0 -80
- data/spec/lib/hamster/hash/values_at_spec.rb +0 -14
- data/spec/lib/hamster/hash/values_spec.rb +0 -25
- data/spec/lib/hamster/list/add_spec.rb +0 -26
- data/spec/lib/hamster/list/all_spec.rb +0 -58
- data/spec/lib/hamster/list/any_spec.rb +0 -50
- data/spec/lib/hamster/list/at_spec.rb +0 -30
- data/spec/lib/hamster/list/break_spec.rb +0 -70
- data/spec/lib/hamster/list/combination_spec.rb +0 -34
- data/spec/lib/hamster/list/compare_spec.rb +0 -31
- data/spec/lib/hamster/list/cons_spec.rb +0 -26
- data/spec/lib/hamster/list/construction_spec.rb +0 -111
- data/spec/lib/hamster/list/count_spec.rb +0 -37
- data/spec/lib/hamster/list/cycle_spec.rb +0 -29
- data/spec/lib/hamster/list/delete_at_spec.rb +0 -19
- data/spec/lib/hamster/list/delete_spec.rb +0 -17
- data/spec/lib/hamster/list/drop_while_spec.rb +0 -39
- data/spec/lib/hamster/list/each_slice_spec.rb +0 -52
- data/spec/lib/hamster/list/each_spec.rb +0 -41
- data/spec/lib/hamster/list/each_with_index_spec.rb +0 -29
- data/spec/lib/hamster/list/empty_spec.rb +0 -24
- data/spec/lib/hamster/list/eql_spec.rb +0 -62
- data/spec/lib/hamster/list/fill_spec.rb +0 -50
- data/spec/lib/hamster/list/index_spec.rb +0 -34
- data/spec/lib/hamster/list/indices_spec.rb +0 -62
- data/spec/lib/hamster/list/inits_spec.rb +0 -29
- data/spec/lib/hamster/list/insert_spec.rb +0 -47
- data/spec/lib/hamster/list/inspect_spec.rb +0 -30
- data/spec/lib/hamster/list/join_spec.rb +0 -64
- data/spec/lib/hamster/list/last_spec.rb +0 -24
- data/spec/lib/hamster/list/ltlt_spec.rb +0 -20
- data/spec/lib/hamster/list/maximum_spec.rb +0 -40
- data/spec/lib/hamster/list/minimum_spec.rb +0 -40
- data/spec/lib/hamster/list/multithreading_spec.rb +0 -48
- data/spec/lib/hamster/list/partition_spec.rb +0 -116
- data/spec/lib/hamster/list/permutation_spec.rb +0 -56
- data/spec/lib/hamster/list/product_spec.rb +0 -24
- data/spec/lib/hamster/list/reduce_spec.rb +0 -54
- data/spec/lib/hamster/list/rotate_spec.rb +0 -37
- data/spec/lib/hamster/list/sample_spec.rb +0 -14
- data/spec/lib/hamster/list/slice_spec.rb +0 -230
- data/spec/lib/hamster/list/span_spec.rb +0 -77
- data/spec/lib/hamster/list/subsequences_spec.rb +0 -24
- data/spec/lib/hamster/list/sum_spec.rb +0 -24
- data/spec/lib/hamster/list/tail_spec.rb +0 -31
- data/spec/lib/hamster/list/tails_spec.rb +0 -29
- data/spec/lib/hamster/list/to_a_spec.rb +0 -40
- data/spec/lib/hamster/list/to_set_spec.rb +0 -19
- data/spec/lib/hamster/list/transpose_spec.rb +0 -20
- data/spec/lib/hamster/list/uniq_spec.rb +0 -30
- data/spec/lib/hamster/nested/construction_spec.rb +0 -44
- data/spec/lib/hamster/set/add_spec.rb +0 -76
- data/spec/lib/hamster/set/all_spec.rb +0 -52
- data/spec/lib/hamster/set/any_spec.rb +0 -52
- data/spec/lib/hamster/set/clear_spec.rb +0 -34
- data/spec/lib/hamster/set/delete_spec.rb +0 -72
- data/spec/lib/hamster/set/difference_spec.rb +0 -50
- data/spec/lib/hamster/set/disjoint_spec.rb +0 -26
- data/spec/lib/hamster/set/each_spec.rb +0 -46
- data/spec/lib/hamster/set/empty_spec.rb +0 -45
- data/spec/lib/hamster/set/exclusion_spec.rb +0 -48
- data/spec/lib/hamster/set/first_spec.rb +0 -29
- data/spec/lib/hamster/set/flatten_spec.rb +0 -47
- data/spec/lib/hamster/set/group_by_spec.rb +0 -60
- data/spec/lib/hamster/set/hash_spec.rb +0 -23
- data/spec/lib/hamster/set/include_spec.rb +0 -61
- data/spec/lib/hamster/set/inspect_spec.rb +0 -48
- data/spec/lib/hamster/set/intersect_spec.rb +0 -26
- data/spec/lib/hamster/set/intersection_spec.rb +0 -53
- data/spec/lib/hamster/set/join_spec.rb +0 -65
- data/spec/lib/hamster/set/map_spec.rb +0 -60
- data/spec/lib/hamster/set/maximum_spec.rb +0 -37
- data/spec/lib/hamster/set/minimum_spec.rb +0 -37
- data/spec/lib/hamster/set/new_spec.rb +0 -54
- data/spec/lib/hamster/set/partition_spec.rb +0 -53
- data/spec/lib/hamster/set/reduce_spec.rb +0 -56
- data/spec/lib/hamster/set/reject_spec.rb +0 -51
- data/spec/lib/hamster/set/sample_spec.rb +0 -14
- data/spec/lib/hamster/set/select_spec.rb +0 -74
- data/spec/lib/hamster/set/subset_spec.rb +0 -52
- data/spec/lib/hamster/set/sum_spec.rb +0 -24
- data/spec/lib/hamster/set/superset_spec.rb +0 -52
- data/spec/lib/hamster/set/to_a_spec.rb +0 -31
- data/spec/lib/hamster/set/to_list_spec.rb +0 -37
- data/spec/lib/hamster/set/union_spec.rb +0 -64
- data/spec/lib/hamster/sorted_set/above_spec.rb +0 -52
- data/spec/lib/hamster/sorted_set/add_spec.rb +0 -63
- data/spec/lib/hamster/sorted_set/at_spec.rb +0 -25
- data/spec/lib/hamster/sorted_set/below_spec.rb +0 -52
- data/spec/lib/hamster/sorted_set/between_spec.rb +0 -52
- data/spec/lib/hamster/sorted_set/clear_spec.rb +0 -44
- data/spec/lib/hamster/sorted_set/construction_spec.rb +0 -29
- data/spec/lib/hamster/sorted_set/delete_at_spec.rb +0 -19
- data/spec/lib/hamster/sorted_set/delete_spec.rb +0 -90
- data/spec/lib/hamster/sorted_set/disjoint_spec.rb +0 -26
- data/spec/lib/hamster/sorted_set/drop_spec.rb +0 -56
- data/spec/lib/hamster/sorted_set/drop_while_spec.rb +0 -35
- data/spec/lib/hamster/sorted_set/each_spec.rb +0 -29
- data/spec/lib/hamster/sorted_set/empty_spec.rb +0 -35
- data/spec/lib/hamster/sorted_set/eql_spec.rb +0 -121
- data/spec/lib/hamster/sorted_set/fetch_spec.rb +0 -65
- data/spec/lib/hamster/sorted_set/find_index_spec.rb +0 -41
- data/spec/lib/hamster/sorted_set/first_spec.rb +0 -19
- data/spec/lib/hamster/sorted_set/from_spec.rb +0 -52
- data/spec/lib/hamster/sorted_set/group_by_spec.rb +0 -58
- data/spec/lib/hamster/sorted_set/include_spec.rb +0 -24
- data/spec/lib/hamster/sorted_set/inspect_spec.rb +0 -38
- data/spec/lib/hamster/sorted_set/intersect_spec.rb +0 -26
- data/spec/lib/hamster/sorted_set/intersection_spec.rb +0 -29
- data/spec/lib/hamster/sorted_set/last_spec.rb +0 -37
- data/spec/lib/hamster/sorted_set/map_spec.rb +0 -36
- data/spec/lib/hamster/sorted_set/marshal_spec.rb +0 -37
- data/spec/lib/hamster/sorted_set/maximum_spec.rb +0 -37
- data/spec/lib/hamster/sorted_set/minimum_spec.rb +0 -20
- data/spec/lib/hamster/sorted_set/new_spec.rb +0 -52
- data/spec/lib/hamster/sorted_set/reverse_each_spec.rb +0 -29
- data/spec/lib/hamster/sorted_set/sample_spec.rb +0 -14
- data/spec/lib/hamster/sorted_set/select_spec.rb +0 -62
- data/spec/lib/hamster/sorted_set/size_spec.rb +0 -18
- data/spec/lib/hamster/sorted_set/slice_spec.rb +0 -257
- data/spec/lib/hamster/sorted_set/sorting_spec.rb +0 -45
- data/spec/lib/hamster/sorted_set/subset_spec.rb +0 -48
- data/spec/lib/hamster/sorted_set/superset_spec.rb +0 -48
- data/spec/lib/hamster/sorted_set/take_spec.rb +0 -55
- data/spec/lib/hamster/sorted_set/take_while_spec.rb +0 -34
- data/spec/lib/hamster/sorted_set/to_set_spec.rb +0 -19
- data/spec/lib/hamster/sorted_set/union_spec.rb +0 -28
- data/spec/lib/hamster/sorted_set/up_to_spec.rb +0 -52
- data/spec/lib/hamster/sorted_set/values_at_spec.rb +0 -34
- data/spec/lib/hamster/vector/add_spec.rb +0 -68
- data/spec/lib/hamster/vector/assoc_spec.rb +0 -36
- data/spec/lib/hamster/vector/bsearch_spec.rb +0 -58
- data/spec/lib/hamster/vector/clear_spec.rb +0 -34
- data/spec/lib/hamster/vector/combination_spec.rb +0 -82
- data/spec/lib/hamster/vector/compact_spec.rb +0 -30
- data/spec/lib/hamster/vector/compare_spec.rb +0 -32
- data/spec/lib/hamster/vector/concat_spec.rb +0 -35
- data/spec/lib/hamster/vector/count_spec.rb +0 -18
- data/spec/lib/hamster/vector/delete_at_spec.rb +0 -54
- data/spec/lib/hamster/vector/delete_spec.rb +0 -31
- data/spec/lib/hamster/vector/drop_spec.rb +0 -42
- data/spec/lib/hamster/vector/drop_while_spec.rb +0 -55
- data/spec/lib/hamster/vector/each_index_spec.rb +0 -41
- data/spec/lib/hamster/vector/each_spec.rb +0 -45
- data/spec/lib/hamster/vector/each_with_index_spec.rb +0 -40
- data/spec/lib/hamster/vector/empty_spec.rb +0 -42
- data/spec/lib/hamster/vector/eql_spec.rb +0 -77
- data/spec/lib/hamster/vector/fetch_spec.rb +0 -65
- data/spec/lib/hamster/vector/fill_spec.rb +0 -89
- data/spec/lib/hamster/vector/first_spec.rb +0 -19
- data/spec/lib/hamster/vector/flat_map_spec.rb +0 -51
- data/spec/lib/hamster/vector/flatten_spec.rb +0 -44
- data/spec/lib/hamster/vector/get_spec.rb +0 -75
- data/spec/lib/hamster/vector/group_by_spec.rb +0 -58
- data/spec/lib/hamster/vector/insert_spec.rb +0 -69
- data/spec/lib/hamster/vector/inspect_spec.rb +0 -50
- data/spec/lib/hamster/vector/map_spec.rb +0 -52
- data/spec/lib/hamster/vector/marshal_spec.rb +0 -32
- data/spec/lib/hamster/vector/maximum_spec.rb +0 -34
- data/spec/lib/hamster/vector/minimum_spec.rb +0 -34
- data/spec/lib/hamster/vector/multiply_spec.rb +0 -48
- data/spec/lib/hamster/vector/new_spec.rb +0 -51
- data/spec/lib/hamster/vector/partition_spec.rb +0 -53
- data/spec/lib/hamster/vector/permutation_spec.rb +0 -92
- data/spec/lib/hamster/vector/pop_spec.rb +0 -27
- data/spec/lib/hamster/vector/product_spec.rb +0 -71
- data/spec/lib/hamster/vector/reject_spec.rb +0 -44
- data/spec/lib/hamster/vector/repeated_combination_spec.rb +0 -78
- data/spec/lib/hamster/vector/repeated_permutation_spec.rb +0 -94
- data/spec/lib/hamster/vector/reverse_each_spec.rb +0 -32
- data/spec/lib/hamster/vector/reverse_spec.rb +0 -22
- data/spec/lib/hamster/vector/rindex_spec.rb +0 -37
- data/spec/lib/hamster/vector/rotate_spec.rb +0 -74
- data/spec/lib/hamster/vector/sample_spec.rb +0 -14
- data/spec/lib/hamster/vector/select_spec.rb +0 -64
- data/spec/lib/hamster/vector/set_spec.rb +0 -175
- data/spec/lib/hamster/vector/shift_spec.rb +0 -28
- data/spec/lib/hamster/vector/shuffle_spec.rb +0 -44
- data/spec/lib/hamster/vector/slice_spec.rb +0 -241
- data/spec/lib/hamster/vector/sorting_spec.rb +0 -57
- data/spec/lib/hamster/vector/take_spec.rb +0 -43
- data/spec/lib/hamster/vector/take_while_spec.rb +0 -35
- data/spec/lib/hamster/vector/to_set_spec.rb +0 -23
- data/spec/lib/hamster/vector/transpose_spec.rb +0 -49
- data/spec/lib/hamster/vector/uniq_spec.rb +0 -56
- data/spec/lib/hamster/vector/unshift_spec.rb +0 -29
- data/spec/lib/hamster/vector/update_in_spec.rb +0 -83
- data/spec/lib/hamster/vector/values_at_spec.rb +0 -34
- data/spec/lib/hamster/vector/zip_spec.rb +0 -58
data/lib/hamster/immutable.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Hamster
|
2
|
-
# @private
|
3
2
|
module Immutable
|
4
3
|
def self.included(klass)
|
5
4
|
klass.extend(ClassMethods)
|
@@ -8,7 +7,6 @@ module Hamster
|
|
8
7
|
end
|
9
8
|
end
|
10
9
|
|
11
|
-
# @private
|
12
10
|
module ClassMethods
|
13
11
|
def new(*args)
|
14
12
|
super.immutable!
|
@@ -32,7 +30,6 @@ module Hamster
|
|
32
30
|
end
|
33
31
|
end
|
34
32
|
|
35
|
-
# @private
|
36
33
|
module MemoizeMethods
|
37
34
|
def immutable!
|
38
35
|
@__hamster_immutable_memory__ = Object.new
|
@@ -40,7 +37,6 @@ module Hamster
|
|
40
37
|
end
|
41
38
|
end
|
42
39
|
|
43
|
-
# @private
|
44
40
|
module InstanceMethods
|
45
41
|
def immutable!
|
46
42
|
freeze
|
data/lib/hamster/list.rb
CHANGED
@@ -1,110 +1,118 @@
|
|
1
|
+
require "forwardable"
|
1
2
|
require "thread"
|
2
|
-
require "atomic"
|
3
|
-
require "set"
|
4
3
|
|
5
4
|
require "hamster/core_ext/enumerable"
|
6
5
|
require "hamster/undefined"
|
7
6
|
require "hamster/enumerable"
|
7
|
+
require "hamster/tuple"
|
8
|
+
require "hamster/sorter"
|
8
9
|
require "hamster/hash"
|
9
10
|
require "hamster/set"
|
10
11
|
|
11
12
|
module Hamster
|
12
13
|
class << self
|
14
|
+
extend Forwardable
|
13
15
|
|
14
|
-
# Create a list containing the given items
|
16
|
+
# Create a list containing the given items
|
15
17
|
#
|
16
18
|
# @example
|
17
19
|
# list = Hamster.list(:a, :b, :c)
|
18
|
-
# # =>
|
20
|
+
# # => [:a, :b, :c]
|
19
21
|
#
|
20
|
-
# @return [List]
|
22
|
+
# @return [Hamster::List]
|
23
|
+
#
|
24
|
+
# @api public
|
21
25
|
def list(*items)
|
22
26
|
items.to_list
|
23
27
|
end
|
24
28
|
|
25
|
-
# Create a lazy, infinite list
|
29
|
+
# Create a lazy, infinite list
|
26
30
|
#
|
27
|
-
# The given block is called
|
31
|
+
# The given block is repeatedly called to yield the elements of the list.
|
28
32
|
#
|
29
33
|
# @example
|
30
34
|
# Hamster.stream { :hello }.take(3)
|
31
|
-
# # =>
|
35
|
+
# # => [:hello, :hello, :hello]
|
36
|
+
#
|
37
|
+
# @return [Hamster::List]
|
32
38
|
#
|
33
|
-
# @
|
39
|
+
# @api public
|
34
40
|
def stream(&block)
|
35
41
|
return EmptyList unless block_given?
|
36
|
-
|
42
|
+
Stream.new { Sequence.new(yield, stream(&block)) }
|
37
43
|
end
|
38
44
|
|
39
|
-
# Construct a list of consecutive integers
|
45
|
+
# Construct a list of consecutive integers
|
40
46
|
#
|
41
47
|
# @example
|
42
48
|
# Hamster.interval(5,9)
|
43
|
-
# # =>
|
49
|
+
# # => [5, 6, 7, 8, 9]
|
44
50
|
#
|
45
51
|
# @param from [Integer] Start value, inclusive
|
46
52
|
# @param to [Integer] End value, inclusive
|
47
|
-
# @return [List]
|
53
|
+
# @return [Hamster::List]
|
54
|
+
#
|
55
|
+
# @api public
|
48
56
|
def interval(from, to)
|
49
57
|
return EmptyList if from > to
|
50
58
|
interval_exclusive(from, to.next)
|
51
59
|
end
|
60
|
+
def_delegator :self, :interval, :range
|
52
61
|
|
53
62
|
# Create an infinite list repeating the same item indefinitely
|
54
63
|
#
|
55
64
|
# @example
|
56
65
|
# Hamster.repeat(:chunky).take(4)
|
57
|
-
# =>
|
66
|
+
# => [:chunky, :chunky, :chunky, :chunky]
|
58
67
|
#
|
59
|
-
# @
|
68
|
+
# @api public
|
60
69
|
def repeat(item)
|
61
|
-
|
70
|
+
Stream.new { Sequence.new(item, repeat(item)) }
|
62
71
|
end
|
63
72
|
|
64
73
|
# Create a list that contains a given item a fixed number of times
|
65
74
|
#
|
66
75
|
# @example
|
67
76
|
# Hamster.replicate(3).(:hamster)
|
68
|
-
# #=>
|
77
|
+
# #=> [:hamster, :hamster, :hamster]
|
69
78
|
#
|
70
|
-
# @
|
79
|
+
# @api public
|
71
80
|
def replicate(number, item)
|
72
81
|
repeat(item).take(number)
|
73
82
|
end
|
74
83
|
|
75
|
-
# Create an infinite list where each item is
|
76
|
-
# using the provided block
|
84
|
+
# Create an infinite list where each item is based on the previous one
|
77
85
|
#
|
78
86
|
# @example
|
79
|
-
# Hamster.iterate(0) {
|
80
|
-
# # =>
|
87
|
+
# Hamster.iterate(0) {|i| i.next}.take(5)
|
88
|
+
# # => [0, 1, 2, 3, 4]
|
81
89
|
#
|
82
90
|
# @param item [Object] Starting value
|
83
91
|
# @yieldparam [Object] The previous value
|
84
92
|
# @yieldreturn [Object] The next value
|
85
|
-
#
|
93
|
+
#
|
94
|
+
# @api public
|
86
95
|
def iterate(item, &block)
|
87
|
-
|
96
|
+
Stream.new { Sequence.new(item, iterate(yield(item), &block)) }
|
88
97
|
end
|
89
98
|
|
90
|
-
# Turn an
|
91
|
-
# where the values are memoized as they are generated.
|
99
|
+
# Turn an enumerator into a Hamster list
|
92
100
|
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
# will be raised. After the collection is realized, it can be used from other
|
96
|
-
# threads as well.
|
101
|
+
# The result is a lazy collection where the values are memoized as they are
|
102
|
+
# generated.
|
97
103
|
#
|
98
104
|
# @example
|
99
|
-
# def rg; loop { yield rand(100) }; end
|
105
|
+
# def rg ; loop { yield rand(100) } ; end
|
100
106
|
# Hamster.enumerate(to_enum(:rg)).take(10)
|
101
107
|
#
|
102
108
|
# @param enum [Enumerator] The object to iterate over
|
103
|
-
# @return [
|
109
|
+
# @return [Stream]
|
110
|
+
#
|
111
|
+
# @api public
|
104
112
|
def enumerate(enum)
|
105
|
-
|
113
|
+
Stream.new do
|
106
114
|
begin
|
107
|
-
|
115
|
+
Sequence.new(enum.next, enumerate(enum))
|
108
116
|
rescue StopIteration
|
109
117
|
EmptyList
|
110
118
|
end
|
@@ -115,84 +123,50 @@ module Hamster
|
|
115
123
|
|
116
124
|
def interval_exclusive(from, to)
|
117
125
|
return EmptyList if from == to
|
118
|
-
|
126
|
+
Stream.new { Sequence.new(from, interval_exclusive(from.next, to)) }
|
119
127
|
end
|
120
128
|
end
|
121
129
|
|
122
|
-
#
|
123
|
-
#
|
124
|
-
#
|
130
|
+
# Common behavior for lists
|
131
|
+
#
|
132
|
+
# A +Hamster::List+ can be constructed with {Hamster.list Hamster.list}. It
|
133
|
+
# consists of a +head+ (the first element) and a +tail+, containing the rest
|
134
|
+
# of the list.
|
125
135
|
#
|
126
136
|
# This is a singly linked list. Prepending to the list with {List#cons} runs
|
127
137
|
# in constant time. Traversing the list from front to back is efficient,
|
128
|
-
#
|
138
|
+
# indexed access however runs in linear time because the list needs to be
|
129
139
|
# traversed to find the element.
|
130
140
|
#
|
141
|
+
# In practice lists are constructed of a combination of {Sequence}, providing
|
142
|
+
# the basic blocks that are linked, {Stream} for providing laziness, and
|
143
|
+
# {EmptyList} as a terminator.
|
131
144
|
module List
|
145
|
+
extend Forwardable
|
132
146
|
include Enumerable
|
133
147
|
|
134
|
-
# @private
|
135
148
|
CADR = /^c([ad]+)r$/
|
136
149
|
|
137
|
-
|
138
|
-
|
139
|
-
# @example
|
140
|
-
# list = Hamster::List[:a, :b, :c]
|
141
|
-
# # => Hamster::List[:a, :b, :c]
|
142
|
-
#
|
143
|
-
# @return [List]
|
144
|
-
def self.[](*items)
|
145
|
-
items.to_list
|
146
|
-
end
|
150
|
+
def_delegator :self, :head, :first
|
151
|
+
def_delegator :self, :empty?, :null?
|
147
152
|
|
148
|
-
# Return the number of items in this `List`.
|
149
|
-
# @return [Integer]
|
150
153
|
def size
|
151
|
-
|
152
|
-
until list.empty?
|
153
|
-
if list.cached_size?
|
154
|
-
return result + list.size
|
155
|
-
else
|
156
|
-
result += 1
|
157
|
-
end
|
158
|
-
list = list.tail
|
159
|
-
end
|
160
|
-
result
|
154
|
+
reduce(0) { |memo, item| memo.next }
|
161
155
|
end
|
162
|
-
|
156
|
+
def_delegator :self, :size, :length
|
163
157
|
|
164
|
-
# Create a new `List` with `item` added at the front.
|
165
|
-
#
|
166
|
-
# @example
|
167
|
-
# Hamster.list(:b, :c).cons(:a)
|
168
|
-
# # => Hamster::List[:a, :b, :c]
|
169
|
-
#
|
170
|
-
# @param item [Object] The item to add
|
171
|
-
# @return [List]
|
172
158
|
def cons(item)
|
173
|
-
|
159
|
+
Sequence.new(item, self)
|
174
160
|
end
|
175
|
-
|
161
|
+
def_delegator :self, :cons, :>>
|
176
162
|
|
177
|
-
|
178
|
-
# than adding items at the front.
|
179
|
-
#
|
180
|
-
# @example
|
181
|
-
# Hamster.list(:a, :b) << :c
|
182
|
-
# # => Hamster::List[:a, :b, :c]
|
183
|
-
#
|
184
|
-
# @param item [Object] The item to add
|
185
|
-
# @return [List]
|
186
|
-
def <<(item)
|
163
|
+
def add(item)
|
187
164
|
append(Hamster.list(item))
|
188
165
|
end
|
166
|
+
def_delegator :self, :add, :<<
|
189
167
|
|
190
|
-
# Call the given block once for each item in the list, passing each
|
191
|
-
# item from first to last successively to the block.
|
192
|
-
#
|
193
|
-
# @return [self]
|
194
168
|
def each
|
195
|
-
return
|
169
|
+
return self unless block_given?
|
196
170
|
list = self
|
197
171
|
until list.empty?
|
198
172
|
yield(list.head)
|
@@ -200,138 +174,71 @@ module Hamster
|
|
200
174
|
end
|
201
175
|
end
|
202
176
|
|
203
|
-
# Return a lazy list in which each element is derived from the corresponding
|
204
|
-
# element in this `List`, transformed through the given block.
|
205
|
-
#
|
206
|
-
# @example
|
207
|
-
# Hamster.list(3, 2, 1).map { |e| e * e } # => Hamster::List[9, 4, 1]
|
208
|
-
#
|
209
|
-
# @return [List]
|
210
177
|
def map(&block)
|
211
|
-
return
|
212
|
-
|
178
|
+
return self unless block_given?
|
179
|
+
Stream.new do
|
213
180
|
next self if empty?
|
214
|
-
|
181
|
+
Sequence.new(yield(head), tail.map(&block))
|
215
182
|
end
|
216
183
|
end
|
217
|
-
|
184
|
+
def_delegator :self, :map, :collect
|
218
185
|
|
219
|
-
# Return a lazy list which is realized by transforming each item into a `List`,
|
220
|
-
# and flattening the resulting lists.
|
221
|
-
#
|
222
|
-
# @example
|
223
|
-
# Hamster.list(1, 2, 3).flat_map { |x| Hamster.list(x, 100) }
|
224
|
-
# # => Hamster::List[1, 100, 2, 100, 3, 100]
|
225
|
-
#
|
226
|
-
# @return [List]
|
227
186
|
def flat_map(&block)
|
228
|
-
return
|
229
|
-
|
187
|
+
return self unless block_given?
|
188
|
+
Stream.new do
|
230
189
|
next self if empty?
|
231
190
|
head_list = Hamster.list(*yield(head))
|
232
191
|
next tail.flat_map(&block) if head_list.empty?
|
233
|
-
|
192
|
+
Sequence.new(head_list.first, head_list.drop(1).append(tail.flat_map(&block)))
|
234
193
|
end
|
235
194
|
end
|
236
195
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
#
|
244
|
-
# @return [List]
|
245
|
-
def select(&block)
|
246
|
-
return enum_for(:select) unless block_given?
|
247
|
-
LazyList.new do
|
248
|
-
list = self
|
249
|
-
while true
|
250
|
-
break list if list.empty?
|
251
|
-
break Cons.new(list.head, list.tail.select(&block)) if yield(list.head)
|
252
|
-
list = list.tail
|
253
|
-
end
|
196
|
+
def filter(&block)
|
197
|
+
return self unless block_given?
|
198
|
+
Stream.new do
|
199
|
+
next self if empty?
|
200
|
+
next Sequence.new(head, tail.filter(&block)) if yield(head)
|
201
|
+
tail.filter(&block)
|
254
202
|
end
|
255
203
|
end
|
256
|
-
alias :find_all :select
|
257
|
-
alias :keep_if :select
|
258
204
|
|
259
|
-
# Return a lazy list which contains all elements up to, but not including, the
|
260
|
-
# first element for which the block returns `nil` or `false`.
|
261
|
-
#
|
262
|
-
# @example
|
263
|
-
# Hamster.list(1, 3, 5, 7, 6, 4, 2).take_while { |e| e < 5 }
|
264
|
-
# # => Hamster::List[1, 3]
|
265
|
-
#
|
266
|
-
# @return [List, Enumerator]
|
267
205
|
def take_while(&block)
|
268
|
-
return
|
269
|
-
|
206
|
+
return self unless block_given?
|
207
|
+
Stream.new do
|
270
208
|
next self if empty?
|
271
|
-
next
|
209
|
+
next Sequence.new(head, tail.take_while(&block)) if yield(head)
|
272
210
|
EmptyList
|
273
211
|
end
|
274
212
|
end
|
275
213
|
|
276
|
-
# Return a lazy list which contains all elements starting from the
|
277
|
-
# first element for which the block returns `nil` or `false`.
|
278
|
-
#
|
279
|
-
# @example
|
280
|
-
# Hamster.list(1, 3, 5, 7, 6, 4, 2).drop_while { |e| e < 5 }
|
281
|
-
# # => Hamster::List[5, 7, 6, 4, 2]
|
282
|
-
#
|
283
|
-
# @return [List, Enumerator]
|
284
214
|
def drop_while(&block)
|
285
|
-
return
|
286
|
-
|
215
|
+
return self unless block_given?
|
216
|
+
Stream.new do
|
287
217
|
list = self
|
288
218
|
list = list.tail while !list.empty? && yield(list.head)
|
289
219
|
list
|
290
220
|
end
|
291
221
|
end
|
292
222
|
|
293
|
-
# Return a lazy list containing the first `number` items from this `List`.
|
294
|
-
#
|
295
|
-
# @example
|
296
|
-
# Hamster.list(1, 3, 5, 7, 6, 4, 2).take(3)
|
297
|
-
# # => Hamster::List[1, 3, 5]
|
298
|
-
#
|
299
|
-
# @param number [Integer] The number of items to retain
|
300
|
-
# @return [List]
|
301
223
|
def take(number)
|
302
|
-
|
224
|
+
Stream.new do
|
303
225
|
next self if empty?
|
304
|
-
next
|
226
|
+
next Sequence.new(head, tail.take(number - 1)) if number > 0
|
305
227
|
EmptyList
|
306
228
|
end
|
307
229
|
end
|
308
230
|
|
309
|
-
# Return a lazy list containing all but the last item from this `List`.
|
310
|
-
#
|
311
|
-
# @example
|
312
|
-
# Hamster.list("A", "B", "C").pop # => Hamster::List["A", "B"]
|
313
|
-
#
|
314
|
-
# @return [List]
|
315
231
|
def pop
|
316
|
-
|
232
|
+
Stream.new do
|
317
233
|
next self if empty?
|
318
234
|
new_size = size - 1
|
319
|
-
next
|
235
|
+
next Sequence.new(head, tail.take(new_size - 1)) if new_size >= 1
|
320
236
|
EmptyList
|
321
237
|
end
|
322
238
|
end
|
323
239
|
|
324
|
-
# Return a lazy list containing all items after the first `number` items from
|
325
|
-
# this `List`.
|
326
|
-
#
|
327
|
-
# @example
|
328
|
-
# Hamster.list(1, 3, 5, 7, 6, 4, 2).drop(3)
|
329
|
-
# # => Hamster::List[7, 6, 4, 2]
|
330
|
-
#
|
331
|
-
# @param number [Integer] The number of items to skip over
|
332
|
-
# @return [List]
|
333
240
|
def drop(number)
|
334
|
-
|
241
|
+
Stream.new do
|
335
242
|
list = self
|
336
243
|
while !list.empty? && number > 0
|
337
244
|
number -= 1
|
@@ -341,688 +248,224 @@ module Hamster
|
|
341
248
|
end
|
342
249
|
end
|
343
250
|
|
344
|
-
# Return a lazy list with all items from this `List`, followed by all items from
|
345
|
-
# `other`.
|
346
|
-
#
|
347
|
-
# @example
|
348
|
-
# Hamster.list(1, 2, 3).append(Hamster.list(4, 5))
|
349
|
-
# # => Hamster::List[1, 2, 3, 4, 5]
|
350
|
-
#
|
351
|
-
# @param other [List] The list to add onto the end of this one
|
352
|
-
# @return [List]
|
353
251
|
def append(other)
|
354
|
-
|
252
|
+
Stream.new do
|
355
253
|
next other if empty?
|
356
|
-
|
254
|
+
Sequence.new(head, tail.append(other))
|
357
255
|
end
|
358
256
|
end
|
359
|
-
|
360
|
-
|
257
|
+
def_delegator :self, :append, :concat
|
258
|
+
def_delegator :self, :append, :cat
|
259
|
+
def_delegator :self, :append, :+
|
361
260
|
|
362
|
-
# Return a `List` with the same items, but in reverse order.
|
363
|
-
#
|
364
|
-
# @example
|
365
|
-
# Hamster.list("A", "B", "C").reverse # => Hamster::List["C", "B", "A"]
|
366
|
-
#
|
367
|
-
# @return [List]
|
368
261
|
def reverse
|
369
|
-
|
262
|
+
Stream.new { reduce(EmptyList) { |list, item| list.cons(item) } }
|
370
263
|
end
|
371
264
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
#
|
377
|
-
# @example
|
378
|
-
# Hamster.list("A", "B", "C").zip(Hamster.list(1, 2, 3))
|
379
|
-
# # => Hamster::List[Hamster::List["A", 1], Hamster::List["B", 2], Hamster::List["C", 3]]
|
380
|
-
#
|
381
|
-
# @param others [List] The list to zip together with this one
|
382
|
-
# @return [List]
|
383
|
-
def zip(others)
|
384
|
-
LazyList.new do
|
385
|
-
next self if empty? && others.empty?
|
386
|
-
Cons.new(Cons.new(head, Cons.new(others.head)), tail.zip(others.tail))
|
387
|
-
end
|
388
|
-
end
|
389
|
-
|
390
|
-
# Gather the first element of each nested list into a new `List`, then the second
|
391
|
-
# element of each nested list, then the third, and so on. In other words, if each
|
392
|
-
# nested list is a "row", return a lazy list of "columns" instead.
|
393
|
-
#
|
394
|
-
# Although the returned list is lazy, each returned nested list (each "column")
|
395
|
-
# is strict. So while each nested list in the input can be infinite, the parent
|
396
|
-
# `List` must not be, or trying to realize the first element in the output will
|
397
|
-
# cause an infinite loop.
|
398
|
-
#
|
399
|
-
# @example
|
400
|
-
# # First let's create some infinite lists
|
401
|
-
# list1 = Hamster.iterate(1, &:next)
|
402
|
-
# list2 = Hamster.iterate(2) { |n| n * 2 }
|
403
|
-
# list3 = Hamster.iterate(3) { |n| n * 3 }
|
404
|
-
#
|
405
|
-
# # Now we transpose our 3 infinite "rows" into an infinite series of 3-element "columns"
|
406
|
-
# Hamster.list(list1, list2, list3).transpose.take(4)
|
407
|
-
# # => Hamster::List[
|
408
|
-
# # Hamster::List[1, 2, 3],
|
409
|
-
# # Hamster::List[2, 4, 9],
|
410
|
-
# # Hamster::List[3, 8, 27],
|
411
|
-
# # Hamster::List[4, 16, 81]]
|
412
|
-
#
|
413
|
-
# @return [List]
|
414
|
-
def transpose
|
415
|
-
return EmptyList if empty?
|
416
|
-
LazyList.new do
|
417
|
-
next EmptyList if any? { |list| list.empty? }
|
418
|
-
heads, tails = EmptyList, EmptyList
|
419
|
-
reverse_each { |list| heads, tails = heads.cons(list.head), tails.cons(list.tail) }
|
420
|
-
Cons.new(heads, tails.transpose)
|
265
|
+
def zip(other)
|
266
|
+
Stream.new do
|
267
|
+
next self if empty? && other.empty?
|
268
|
+
Sequence.new(Sequence.new(head, Sequence.new(other.head)), tail.zip(other.tail))
|
421
269
|
end
|
422
270
|
end
|
423
271
|
|
424
|
-
# Concatenate an infinite series of copies of this `List` together (into a
|
425
|
-
# new lazy list). Or, if empty, just return an empty list.
|
426
|
-
#
|
427
|
-
# @example
|
428
|
-
# Hamster.list(1, 2, 3).cycle.take(10)
|
429
|
-
# # => Hamster::List[1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
|
430
|
-
#
|
431
|
-
# @return [List]
|
432
272
|
def cycle
|
433
|
-
|
273
|
+
Stream.new do
|
434
274
|
next self if empty?
|
435
|
-
|
275
|
+
Sequence.new(head, tail.append(cycle))
|
436
276
|
end
|
437
277
|
end
|
438
278
|
|
439
|
-
# Return a new `List` with the same elements, but rotated so that the one at
|
440
|
-
# index `count` is the first element of the new list. If `count` is positive,
|
441
|
-
# the elements will be shifted left, and those shifted past the lowest position
|
442
|
-
# will be moved to the end. If `count` is negative, the elements will be shifted
|
443
|
-
# right, and those shifted past the last position will be moved to the beginning.
|
444
|
-
#
|
445
|
-
# @example
|
446
|
-
# l = Hamster.list("A", "B", "C", "D", "E", "F")
|
447
|
-
# l.rotate(2) # => Hamster::List["C", "D", "E", "F", "A", "B"]
|
448
|
-
# l.rotate(-1) # => Hamster::List["F", "A", "B", "C", "D", "E"]
|
449
|
-
#
|
450
|
-
# @param count [Integer] The number of positions to shift items by
|
451
|
-
# @return [Vector]
|
452
|
-
def rotate(count = 1)
|
453
|
-
raise TypeError, "expected Integer" if not count.is_a?(Integer)
|
454
|
-
return self if empty? || (count % size) == 0
|
455
|
-
count = (count >= 0) ? count % size : (size - (~count % size) - 1)
|
456
|
-
drop(count).append(take(count))
|
457
|
-
end
|
458
|
-
|
459
|
-
# Return 2 `List`s, one of the first `number` items, and another of all the
|
460
|
-
# remaining items.
|
461
|
-
#
|
462
|
-
# @example
|
463
|
-
# Hamster.list("a", "b", "c", "d").split(2)
|
464
|
-
# # => [Hamster::List["a", "b"], Hamster::List["c", "d"]]
|
465
|
-
#
|
466
|
-
# @param number [Integer] The index at which to split this list
|
467
|
-
# @return [Array]
|
468
279
|
def split_at(number)
|
469
|
-
|
280
|
+
Tuple.new(take(number), drop(number))
|
470
281
|
end
|
471
282
|
|
472
|
-
# Return 2 `List`s, one up to (but not including) the first item for which the
|
473
|
-
# block returns `nil` or `false`, and another of all the remaining items.
|
474
|
-
#
|
475
|
-
# @example
|
476
|
-
# Hamster.list(4, 3, 5, 2, 1).span { |x| x > 2 }
|
477
|
-
# # => [Hamster::List[4, 3, 5], Hamster::List[2, 1]]
|
478
|
-
#
|
479
|
-
# @return [Array]
|
480
283
|
def span(&block)
|
481
|
-
return
|
482
|
-
|
483
|
-
mutex = Mutex.new
|
484
|
-
[Splitter::Left.new(splitter, splitter.left, mutex),
|
485
|
-
Splitter::Right.new(splitter, mutex)].freeze
|
284
|
+
return Tuple.new(self, EmptyList) unless block_given?
|
285
|
+
Tuple.new(take_while(&block), drop_while(&block))
|
486
286
|
end
|
487
287
|
|
488
|
-
# Return 2 `List`s, one up to (but not including) the first item for which the
|
489
|
-
# block returns true, and another of all the remaining items.
|
490
|
-
#
|
491
|
-
# @example
|
492
|
-
# Hamster.list(1, 3, 4, 2, 5).break { |x| x > 3 }
|
493
|
-
# # => [Hamster::List[1, 3], Hamster::List[4, 2, 5]]
|
494
|
-
#
|
495
|
-
# @return [Array]
|
496
288
|
def break(&block)
|
497
289
|
return span unless block_given?
|
498
290
|
span { |item| !yield(item) }
|
499
291
|
end
|
500
292
|
|
501
|
-
# Return an empty `List`.
|
502
|
-
# @return [List]
|
503
293
|
def clear
|
504
294
|
EmptyList
|
505
295
|
end
|
506
296
|
|
507
|
-
# Return a `List` with the same items, but sorted either in their natural order,
|
508
|
-
# or using an optional comparator block. The block must take 2 parameters, and
|
509
|
-
# return 0, 1, or -1 if the first one is equal, greater than, or less than the
|
510
|
-
# second (respectively).
|
511
|
-
#
|
512
|
-
# @return [List]
|
513
297
|
def sort(&comparator)
|
514
|
-
|
298
|
+
Stream.new { Sorter.new(self).sort(&comparator) }
|
515
299
|
end
|
516
300
|
|
517
|
-
# Return a new `List` with the same items, but sorted. The sort order will be
|
518
|
-
# determined by mapping the items through the given block to obtain sort keys,
|
519
|
-
# and then sorting the keys according to their natural sort order.
|
520
|
-
#
|
521
|
-
# @return [List]
|
522
301
|
def sort_by(&transformer)
|
523
302
|
return sort unless block_given?
|
524
|
-
|
303
|
+
Stream.new { Sorter.new(self).sort_by(&transformer) }
|
304
|
+
end
|
305
|
+
|
306
|
+
def join(sep = "")
|
307
|
+
return "" if empty?
|
308
|
+
sep = sep.to_s
|
309
|
+
tail.reduce(head.to_s.dup) { |result, item| result << sep << item.to_s }
|
525
310
|
end
|
526
311
|
|
527
|
-
# Return a new `List` with `sep` inserted between each of the existing elements.
|
528
|
-
#
|
529
|
-
# @example
|
530
|
-
# Hamster.list("one", "two", "three").intersperse(" ")
|
531
|
-
# # => Hamster::List["one", " ", "two", " ", "three"]
|
532
|
-
#
|
533
|
-
# @return [List]
|
534
312
|
def intersperse(sep)
|
535
|
-
|
313
|
+
Stream.new do
|
536
314
|
next self if tail.empty?
|
537
|
-
|
315
|
+
Sequence.new(head, Sequence.new(sep, tail.intersperse(sep)))
|
538
316
|
end
|
539
317
|
end
|
540
318
|
|
541
|
-
|
542
|
-
|
543
|
-
#
|
544
|
-
# @example
|
545
|
-
# Hamster.list(:a, :b, :a, :c, :b).uniq # => Hamster::List[:a, :b, :c]
|
546
|
-
#
|
547
|
-
# @return [List]
|
548
|
-
def uniq(items = ::Set.new)
|
549
|
-
LazyList.new do
|
319
|
+
def uniq(items = EmptySet)
|
320
|
+
Stream.new do
|
550
321
|
next self if empty?
|
551
322
|
next tail.uniq(items) if items.include?(head)
|
552
|
-
|
323
|
+
Sequence.new(head, tail.uniq(items.add(head)))
|
553
324
|
end
|
554
325
|
end
|
326
|
+
def_delegator :self, :uniq, :nub
|
327
|
+
def_delegator :self, :uniq, :remove_duplicates
|
555
328
|
|
556
|
-
|
557
|
-
|
558
|
-
#
|
559
|
-
# @example
|
560
|
-
# Hamster.list(1, 2).union(Hamster.list(2, 3)) # => Hamster::List[1, 2, 3]
|
561
|
-
#
|
562
|
-
# @param other [List] The list to merge with
|
563
|
-
# @return [List]
|
564
|
-
def union(other, items = ::Set.new)
|
565
|
-
LazyList.new do
|
566
|
-
next other.uniq(items) if empty?
|
567
|
-
next tail.union(other, items) if items.include?(head)
|
568
|
-
Cons.new(head, tail.union(other, items.add(head)))
|
569
|
-
end
|
329
|
+
def union(other)
|
330
|
+
append(other).uniq
|
570
331
|
end
|
571
|
-
|
332
|
+
def_delegator :self, :union, :|
|
572
333
|
|
573
|
-
# Return a lazy list with all elements except the last one.
|
574
|
-
#
|
575
|
-
# @example
|
576
|
-
# Hamster.list("a", "b", "c").init # => Hamster::List["a", "b"]
|
577
|
-
#
|
578
|
-
# @return [List]
|
579
334
|
def init
|
580
335
|
return EmptyList if tail.empty?
|
581
|
-
|
336
|
+
Stream.new { Sequence.new(head, tail.init) }
|
582
337
|
end
|
583
338
|
|
584
|
-
# Return the last item in this list.
|
585
|
-
# @return [Object]
|
586
339
|
def last
|
587
340
|
list = self
|
588
341
|
list = list.tail until list.tail.empty?
|
589
342
|
list.head
|
590
343
|
end
|
591
344
|
|
592
|
-
# Return a lazy list of all suffixes of this list.
|
593
|
-
#
|
594
|
-
# @example
|
595
|
-
# Hamster.list(1,2,3).tails
|
596
|
-
# # => Hamster::List[
|
597
|
-
# # Hamster::List[1, 2, 3],
|
598
|
-
# # Hamster::List[2, 3],
|
599
|
-
# # Hamster::List[3]]
|
600
|
-
#
|
601
|
-
# @return [List]
|
602
345
|
def tails
|
603
|
-
|
604
|
-
next self if empty?
|
605
|
-
|
346
|
+
Stream.new do
|
347
|
+
next Sequence.new(self) if empty?
|
348
|
+
Sequence.new(self, tail.tails)
|
606
349
|
end
|
607
350
|
end
|
608
351
|
|
609
|
-
# Return a lazy list of all prefixes of this list.
|
610
|
-
#
|
611
|
-
# @example
|
612
|
-
# Hamster.list(1,2,3).inits
|
613
|
-
# # => Hamster::List[
|
614
|
-
# # Hamster::List[1],
|
615
|
-
# # Hamster::List[1, 2],
|
616
|
-
# # Hamster::List[1, 2, 3]]
|
617
|
-
#
|
618
|
-
# @return [List]
|
619
352
|
def inits
|
620
|
-
|
621
|
-
next self if empty?
|
622
|
-
|
353
|
+
Stream.new do
|
354
|
+
next Sequence.new(self) if empty?
|
355
|
+
Sequence.new(EmptyList, tail.inits.map { |list| list.cons(head) })
|
623
356
|
end
|
624
357
|
end
|
625
358
|
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
# Hamster.list(1,2,3).combination(2)
|
630
|
-
# # => Hamster::List[
|
631
|
-
# # Hamster::List[1, 2],
|
632
|
-
# # Hamster::List[1, 3],
|
633
|
-
# # Hamster::List[2, 3]]
|
634
|
-
#
|
635
|
-
# @return [List]
|
636
|
-
def combination(n)
|
637
|
-
return Cons.new(EmptyList) if n == 0
|
638
|
-
LazyList.new do
|
359
|
+
def combinations(number)
|
360
|
+
return Sequence.new(EmptyList) if number == 0
|
361
|
+
Stream.new do
|
639
362
|
next self if empty?
|
640
|
-
tail.
|
363
|
+
tail.combinations(number - 1).map { |list| list.cons(head) }.append(tail.combinations(number))
|
641
364
|
end
|
642
365
|
end
|
366
|
+
def_delegator :self, :combinations, :combination
|
643
367
|
|
644
|
-
# Split the items in this list in groups of `number`. Return a list of lists.
|
645
|
-
#
|
646
|
-
# @example
|
647
|
-
# Hamster.list(*("a".."o")).chunk(5)
|
648
|
-
# # => Hamster::List[
|
649
|
-
# # Hamster::List["a", "b", "c", "d", "e"],
|
650
|
-
# # Hamster::List["f", "g", "h", "i", "j"],
|
651
|
-
# # Hamster::List["k", "l", "m", "n", "o"]]
|
652
|
-
#
|
653
|
-
# @return [List]
|
654
368
|
def chunk(number)
|
655
|
-
|
369
|
+
Stream.new do
|
656
370
|
next self if empty?
|
657
371
|
first, remainder = split_at(number)
|
658
|
-
|
372
|
+
Sequence.new(first, remainder.chunk(number))
|
659
373
|
end
|
660
374
|
end
|
661
375
|
|
662
|
-
# Split the items in this list in groups of `number`, and yield each group
|
663
|
-
# to the block (as a `List`).
|
664
|
-
# @return [self]
|
665
376
|
def each_chunk(number, &block)
|
666
|
-
return enum_for(:each_chunk, number) unless block_given?
|
667
377
|
chunk(number).each(&block)
|
668
|
-
self
|
669
378
|
end
|
670
|
-
|
379
|
+
def_delegator :self, :each_chunk, :each_slice
|
671
380
|
|
672
|
-
# Return a new `List` with all nested lists recursively "flattened out",
|
673
|
-
# that is, their elements inserted into the new `List` in the place where
|
674
|
-
# the nested list originally was.
|
675
|
-
#
|
676
|
-
# @example
|
677
|
-
# Hamster.list(Hamster.list(1, 2), Hamster.list(3, 4)).flatten
|
678
|
-
# # => Hamster::List[1, 2, 3, 4]
|
679
|
-
#
|
680
|
-
# @return [List]
|
681
381
|
def flatten
|
682
|
-
|
382
|
+
Stream.new do
|
683
383
|
next self if empty?
|
684
384
|
next head.append(tail.flatten) if head.is_a?(List)
|
685
|
-
|
385
|
+
Sequence.new(head, tail.flatten)
|
686
386
|
end
|
687
387
|
end
|
688
388
|
|
689
|
-
# Passes each item to the block, and gathers them into a {Hash} where the
|
690
|
-
# keys are return values from the block, and the values are `List`s of items
|
691
|
-
# for which the block returned that value.
|
692
|
-
#
|
693
|
-
# @return [Hash]
|
694
389
|
def group_by(&block)
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
# Retrieve the item at `index`. Negative indices count back from the end of
|
700
|
-
# the list (-1 is the last item). If `index` is invalid (either too high or
|
701
|
-
# too low), return `nil`.
|
702
|
-
#
|
703
|
-
# @param index [Integer] The index to retrieve
|
704
|
-
# @return [Object]
|
705
|
-
def at(index)
|
706
|
-
index += size if index < 0
|
707
|
-
return nil if index < 0
|
708
|
-
node = self
|
709
|
-
while index > 0
|
710
|
-
node = node.tail
|
711
|
-
index -= 1
|
390
|
+
return group_by { |item| item } unless block_given?
|
391
|
+
reduce(EmptyHash) do |hash, item|
|
392
|
+
key = yield(item)
|
393
|
+
hash.put(key, (hash.get(key) || EmptyList).cons(item))
|
712
394
|
end
|
713
|
-
node.head
|
714
395
|
end
|
396
|
+
def_delegator :self, :group_by, :group
|
715
397
|
|
716
|
-
|
717
|
-
|
718
|
-
#
|
719
|
-
# @overload list[index]
|
720
|
-
# Return the item at `index`.
|
721
|
-
# @param index [Integer] The index to retrieve.
|
722
|
-
# @overload list[start, length]
|
723
|
-
# Return a sublist starting at index `start` and continuing for `length` elements.
|
724
|
-
# @param start [Integer] The index to start retrieving items from.
|
725
|
-
# @param length [Integer] The number of items to retrieve.
|
726
|
-
# @overload list[range]
|
727
|
-
# Return a sublist specified by the given `range` of indices.
|
728
|
-
# @param range [Range] The range of indices to retrieve.
|
729
|
-
#
|
730
|
-
# @return [Object]
|
731
|
-
def [](arg, length = (missing_length = true))
|
732
|
-
if missing_length
|
733
|
-
if arg.is_a?(Range)
|
734
|
-
from, to = arg.begin, arg.end
|
735
|
-
from += size if from < 0
|
736
|
-
return nil if from < 0
|
737
|
-
to += size if to < 0
|
738
|
-
to += 1 if !arg.exclude_end?
|
739
|
-
length = to - from
|
740
|
-
length = 0 if length < 0
|
741
|
-
list = self
|
742
|
-
while from > 0
|
743
|
-
return nil if list.empty?
|
744
|
-
list = list.tail
|
745
|
-
from -= 1
|
746
|
-
end
|
747
|
-
list.take(length)
|
748
|
-
else
|
749
|
-
at(arg)
|
750
|
-
end
|
751
|
-
else
|
752
|
-
return nil if length < 0
|
753
|
-
arg += size if arg < 0
|
754
|
-
return nil if arg < 0
|
755
|
-
list = self
|
756
|
-
while arg > 0
|
757
|
-
return nil if list.empty?
|
758
|
-
list = list.tail
|
759
|
-
arg -= 1
|
760
|
-
end
|
761
|
-
list.take(length)
|
762
|
-
end
|
398
|
+
def at(index)
|
399
|
+
drop(index).head
|
763
400
|
end
|
764
|
-
alias :slice :[]
|
765
401
|
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
# @overload indices(obj)
|
770
|
-
# Return a `List` of indices where `obj` is found. Use `#==` for testing equality.
|
771
|
-
# @overload indices { |item| ... }
|
772
|
-
# Pass each item successively to the block. Return a list of indices where the
|
773
|
-
# block returns true.
|
774
|
-
#
|
775
|
-
# @return [List]
|
776
|
-
def indices(object = Undefined, i = 0, &block)
|
777
|
-
return indices { |item| item == object } if not block_given?
|
778
|
-
return EmptyList if empty?
|
779
|
-
LazyList.new do
|
780
|
-
node = self
|
781
|
-
while true
|
782
|
-
break Cons.new(i, node.tail.indices(Undefined, i + 1, &block)) if yield(node.head)
|
783
|
-
node = node.tail
|
784
|
-
break EmptyList if node.empty?
|
785
|
-
i += 1
|
786
|
-
end
|
787
|
-
end
|
402
|
+
def slice(from, length = Undefined)
|
403
|
+
return at(from) if length.equal?(Undefined)
|
404
|
+
drop(from).take(length)
|
788
405
|
end
|
406
|
+
def_delegator :self, :slice, :[]
|
789
407
|
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
# list_1 = Hamster::List[1, -3, -5]
|
800
|
-
# list_2 = Hamster::List[-2, 4, 6]
|
801
|
-
# Hamster::List[list_1, list_2].merge { |a,b| a.abs <=> b.abs }
|
802
|
-
# # => Hamster::List[1, -2, -3, 4, -5, 6]
|
803
|
-
#
|
804
|
-
# @return [List]
|
805
|
-
def merge(&comparator)
|
806
|
-
return merge_by unless block_given?
|
807
|
-
LazyList.new do
|
808
|
-
sorted = reject(&:empty?).sort do |a, b|
|
809
|
-
yield(a.head, b.head)
|
810
|
-
end
|
811
|
-
next EmptyList if sorted.empty?
|
812
|
-
Cons.new(sorted.head.head, sorted.tail.cons(sorted.head.tail).merge(&comparator))
|
408
|
+
def find_index
|
409
|
+
return nil unless block_given?
|
410
|
+
i = 0
|
411
|
+
list = self
|
412
|
+
loop do
|
413
|
+
return nil if list.empty?
|
414
|
+
return i if yield(list.head)
|
415
|
+
i += 1
|
416
|
+
list = list.tail
|
813
417
|
end
|
814
418
|
end
|
815
419
|
|
816
|
-
|
817
|
-
|
818
|
-
# order which items should be shifted out of the nested lists and into the output
|
819
|
-
# list. Whichever nested list's `#head` has the "lowest" sort key (according to
|
820
|
-
# their natural order) will be the first in the merged `List`.
|
821
|
-
#
|
822
|
-
# @example
|
823
|
-
# list_1 = Hamster::List[1, -3, -5]
|
824
|
-
# list_2 = Hamster::List[-2, 4, 6]
|
825
|
-
# Hamster::List[list_1, list_2].merge_by { |x| x.abs }
|
826
|
-
# # => Hamster::List[1, -2, -3, 4, -5, 6]
|
827
|
-
#
|
828
|
-
# @return [List]
|
829
|
-
def merge_by(&transformer)
|
830
|
-
return merge_by { |item| item } unless block_given?
|
831
|
-
LazyList.new do
|
832
|
-
sorted = reject(&:empty?).sort_by do |list|
|
833
|
-
yield(list.head)
|
834
|
-
end
|
835
|
-
next EmptyList if sorted.empty?
|
836
|
-
Cons.new(sorted.head.head, sorted.tail.cons(sorted.head.tail).merge_by(&transformer))
|
837
|
-
end
|
420
|
+
def elem_index(object)
|
421
|
+
find_index { |item| item == object }
|
838
422
|
end
|
839
423
|
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
at(rand(size))
|
424
|
+
def index(object = Undefined, &block)
|
425
|
+
return elem_index(object) unless object.equal?(Undefined)
|
426
|
+
find_index(&block)
|
844
427
|
end
|
845
428
|
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
# @param items [Array] The items to add
|
853
|
-
# @return [List]
|
854
|
-
def insert(index, *items)
|
855
|
-
if index == 0
|
856
|
-
return items.to_list.append(self)
|
857
|
-
elsif index > 0
|
858
|
-
LazyList.new do
|
859
|
-
Cons.new(head, tail.insert(index-1, *items))
|
860
|
-
end
|
861
|
-
else
|
862
|
-
raise IndexError if index < -size
|
863
|
-
insert(index + size, *items)
|
429
|
+
def find_indices(i = 0, &block)
|
430
|
+
return EmptyList unless block_given?
|
431
|
+
Stream.new do
|
432
|
+
next EmptyList if empty?
|
433
|
+
next Sequence.new(i, tail.find_indices(i + 1, &block)) if yield(head)
|
434
|
+
tail.find_indices(i + 1, &block)
|
864
435
|
end
|
865
436
|
end
|
866
437
|
|
867
|
-
|
868
|
-
|
869
|
-
#
|
870
|
-
# @example
|
871
|
-
# Hamster.list(:a, :b, :a, :a, :c).delete(:a) # => Hamster::List[:b, :c]
|
872
|
-
#
|
873
|
-
# @param obj [Object] The object to remove.
|
874
|
-
# @return [List]
|
875
|
-
def delete(obj)
|
876
|
-
list = self
|
877
|
-
list = list.tail while list.head == obj && !list.empty?
|
878
|
-
return EmptyList if list.empty?
|
879
|
-
LazyList.new { Cons.new(list.head, list.tail.delete(obj)) }
|
880
|
-
end
|
881
|
-
|
882
|
-
# Return a lazy list containing the same items, minus the one at `index`.
|
883
|
-
# If `index` is negative, it counts back from the end of the list.
|
884
|
-
#
|
885
|
-
# @example
|
886
|
-
# Hamster.list(1, 2, 3).delete_at(1) # => Hamster::List[1, 3]
|
887
|
-
# Hamster.list(1, 2, 3).delete_at(-1) # => Hamster::List[1, 2]
|
888
|
-
#
|
889
|
-
# @param index [Integer] The index of the item to remove
|
890
|
-
# @return [List]
|
891
|
-
def delete_at(index)
|
892
|
-
if index == 0
|
893
|
-
tail
|
894
|
-
elsif index < 0
|
895
|
-
index += size if index < 0
|
896
|
-
return self if index < 0
|
897
|
-
delete_at(index)
|
898
|
-
else
|
899
|
-
LazyList.new { Cons.new(head, tail.delete_at(index - 1)) }
|
900
|
-
end
|
438
|
+
def elem_indices(object)
|
439
|
+
find_indices { |item| item == object }
|
901
440
|
end
|
902
441
|
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
# Return a new `List` of the same size, with every item set to `obj`.
|
907
|
-
# @overload fill(obj, start)
|
908
|
-
# Return a new `List` with all indexes from `start` to the end of the
|
909
|
-
# list set to `obj`.
|
910
|
-
# @overload fill(obj, start, length)
|
911
|
-
# Return a new `List` with `length` indexes, beginning from `start`,
|
912
|
-
# set to `obj`.
|
913
|
-
#
|
914
|
-
# @example
|
915
|
-
# list = Hamster.list("a", "b", "c", "d")
|
916
|
-
# list.fill("x") # => Hamster::List["x", "x", "x", "x"]
|
917
|
-
# list.fill("z", 2) # => Hamster::List["a", "b", "z", "z"]
|
918
|
-
# list.fill("y", 0, 2) # => Hamster::List["y", "y", "c", "d"]
|
919
|
-
#
|
920
|
-
# @return [List]
|
921
|
-
def fill(obj, index = 0, length = nil)
|
922
|
-
if index == 0
|
923
|
-
length ||= size
|
924
|
-
if length > 0
|
925
|
-
LazyList.new do
|
926
|
-
Cons.new(obj, tail.fill(obj, 0, length-1))
|
927
|
-
end
|
928
|
-
else
|
929
|
-
self
|
930
|
-
end
|
931
|
-
elsif index > 0
|
932
|
-
LazyList.new do
|
933
|
-
Cons.new(head, tail.fill(obj, index-1, length))
|
934
|
-
end
|
935
|
-
else
|
936
|
-
raise IndexError if index < -size
|
937
|
-
fill(obj, index + size, length)
|
938
|
-
end
|
442
|
+
def indices(object = Undefined, &block)
|
443
|
+
return elem_indices(object) unless object.equal?(Undefined)
|
444
|
+
find_indices(&block)
|
939
445
|
end
|
940
446
|
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
#
|
947
|
-
# If no block is given, an `Enumerator` is returned instead.
|
948
|
-
#
|
949
|
-
# @example
|
950
|
-
# Hamster.list(1, 2, 3).permutation.to_a
|
951
|
-
# # => [Hamster::List[1, 2, 3],
|
952
|
-
# # Hamster::List[2, 1, 3],
|
953
|
-
# # Hamster::List[2, 3, 1],
|
954
|
-
# # Hamster::List[1, 3, 2],
|
955
|
-
# # Hamster::List[3, 1, 2],
|
956
|
-
# # Hamster::List[3, 2, 1]]
|
957
|
-
#
|
958
|
-
# @return [self, Enumerator]
|
959
|
-
def permutation(length = size, &block)
|
960
|
-
return enum_for(:permutation, length) if not block_given?
|
961
|
-
if length == 0
|
962
|
-
yield EmptyList
|
963
|
-
elsif length == 1
|
964
|
-
each { |obj| yield Cons.new(obj, EmptyList) }
|
965
|
-
elsif not empty?
|
966
|
-
if length < size
|
967
|
-
tail.permutation(length, &block)
|
968
|
-
end
|
969
|
-
|
970
|
-
tail.permutation(length-1) do |p|
|
971
|
-
0.upto(length-1) do |i|
|
972
|
-
left,right = p.split_at(i)
|
973
|
-
yield left.append(right.cons(head))
|
974
|
-
end
|
447
|
+
def merge(&comparator)
|
448
|
+
return merge_by unless block_given?
|
449
|
+
Stream.new do
|
450
|
+
sorted = remove(&:empty?).sort do |a, b|
|
451
|
+
yield(a.head, b.head)
|
975
452
|
end
|
453
|
+
next EmptyList if sorted.empty?
|
454
|
+
Sequence.new(sorted.head.head, sorted.tail.cons(sorted.head.tail).merge(&comparator))
|
976
455
|
end
|
977
|
-
self
|
978
456
|
end
|
979
457
|
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
# # prints:
|
986
|
-
# # Hamster::List[1]
|
987
|
-
# # Hamster::List[1, 2]
|
988
|
-
# # Hamster::List[1, 2, 3]
|
989
|
-
# # Hamster::List[2]
|
990
|
-
# # Hamster::List[2, 3]
|
991
|
-
# # Hamster::List[3]
|
992
|
-
#
|
993
|
-
# @yield [sublist] One or more contiguous elements from this list
|
994
|
-
# @return [self]
|
995
|
-
def subsequences(&block)
|
996
|
-
return enum_for(:subsequences) if not block_given?
|
997
|
-
if not empty?
|
998
|
-
1.upto(size) do |n|
|
999
|
-
yield take(n)
|
458
|
+
def merge_by(&transformer)
|
459
|
+
return merge_by { |item| item } unless block_given?
|
460
|
+
Stream.new do
|
461
|
+
sorted = remove(&:empty?).sort_by do |list|
|
462
|
+
yield(list.head)
|
1000
463
|
end
|
1001
|
-
|
464
|
+
next EmptyList if sorted.empty?
|
465
|
+
Sequence.new(sorted.head.head, sorted.tail.cons(sorted.head.tail).merge_by(&transformer))
|
1002
466
|
end
|
1003
|
-
self
|
1004
467
|
end
|
1005
468
|
|
1006
|
-
# Return 2 `List`s, the first containing all the elements for which the block
|
1007
|
-
# evaluates to true, the second containing the rest.
|
1008
|
-
#
|
1009
|
-
# @example
|
1010
|
-
# Hamster.list(1, 2, 3, 4, 5, 6).partition { |x| x.even? }
|
1011
|
-
# # => [Hamster::List[2, 4, 6], Hamster::List[1, 3, 5]]
|
1012
|
-
#
|
1013
|
-
# @return [List]
|
1014
|
-
def partition(&block)
|
1015
|
-
return enum_for(:partition) if not block_given?
|
1016
|
-
partitioner = Partitioner.new(self, block)
|
1017
|
-
mutex = Mutex.new
|
1018
|
-
[Partitioned.new(partitioner, partitioner.left, mutex),
|
1019
|
-
Partitioned.new(partitioner, partitioner.right, mutex)].freeze
|
1020
|
-
end
|
1021
|
-
|
1022
|
-
# Return true if `other` has the same type and contents as this `Hash`.
|
1023
|
-
#
|
1024
|
-
# @param other [Object] The collection to compare with
|
1025
|
-
# @return [Boolean]
|
1026
469
|
def eql?(other)
|
1027
470
|
list = self
|
1028
471
|
loop do
|
@@ -1035,96 +478,65 @@ module Hamster
|
|
1035
478
|
other = other.tail
|
1036
479
|
end
|
1037
480
|
end
|
481
|
+
def_delegator :self, :eql?, :==
|
1038
482
|
|
1039
|
-
# See `Object#hash`
|
1040
|
-
# @return [Integer]
|
1041
483
|
def hash
|
1042
484
|
reduce(0) { |hash, item| (hash << 5) - hash + item.hash }
|
1043
485
|
end
|
1044
486
|
|
1045
|
-
# Return `self`.
|
1046
|
-
# @return [List]
|
1047
487
|
def dup
|
1048
488
|
self
|
1049
489
|
end
|
1050
|
-
|
490
|
+
def_delegator :self, :dup, :clone
|
1051
491
|
|
1052
|
-
# Return `self`.
|
1053
|
-
# @return [List]
|
1054
492
|
def to_list
|
1055
493
|
self
|
1056
494
|
end
|
1057
495
|
|
1058
|
-
|
1059
|
-
|
1060
|
-
# be passed to `eval` to reconstitute an equivalent `List`.
|
1061
|
-
#
|
1062
|
-
# @return [String]
|
1063
|
-
def inspect
|
1064
|
-
result = "Hamster::List["
|
1065
|
-
each_with_index { |obj, i| result << ', ' if i > 0; result << obj.inspect }
|
1066
|
-
result << "]"
|
496
|
+
def to_set
|
497
|
+
reduce(EmptySet) { |set, item| set.add(item) }
|
1067
498
|
end
|
1068
499
|
|
1069
|
-
|
1070
|
-
|
1071
|
-
# the screen into account, and which indents nested structures to make them easier
|
1072
|
-
# to read.
|
1073
|
-
#
|
1074
|
-
# @private
|
1075
|
-
def pretty_print(pp)
|
1076
|
-
pp.group(1, "Hamster::List[", "]") do
|
1077
|
-
pp.breakable ''
|
1078
|
-
pp.seplist(self) { |obj| obj.pretty_print(pp) }
|
1079
|
-
end
|
500
|
+
def inspect
|
501
|
+
to_a.inspect
|
1080
502
|
end
|
1081
503
|
|
1082
|
-
# @private
|
1083
504
|
def respond_to?(name, include_private = false)
|
1084
505
|
super || !!name.to_s.match(CADR)
|
1085
506
|
end
|
1086
507
|
|
1087
|
-
# Return `true` if the size of this list can be obtained in constant time (without
|
1088
|
-
# traversing the list).
|
1089
|
-
# @return [Integer]
|
1090
|
-
def cached_size?
|
1091
|
-
false
|
1092
|
-
end
|
1093
|
-
|
1094
508
|
private
|
1095
509
|
|
1096
510
|
def method_missing(name, *args, &block)
|
1097
|
-
if name.to_s.match(CADR)
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
super
|
511
|
+
return accessor(Regexp.last_match[1]) if name.to_s.match(CADR)
|
512
|
+
super
|
513
|
+
end
|
514
|
+
|
515
|
+
# Perform compositions of <tt>car</tt> and <tt>cdr</tt> operations. Their names consist of a 'c', followed by at
|
516
|
+
# least one 'a' or 'd', and finally an 'r'. The series of 'a's and 'd's in each function's name is chosen to
|
517
|
+
# identify the series of car and cdr operations that is performed by the function. The order in which the 'a's and
|
518
|
+
# 'd's appear is the inverse of the order in which the corresponding operations are performed.
|
519
|
+
def accessor(sequence)
|
520
|
+
sequence.reverse.each_char.reduce(self) do |memo, char|
|
521
|
+
case char
|
522
|
+
when "a" then memo.head
|
523
|
+
when "d" then memo.tail
|
524
|
+
end
|
1112
525
|
end
|
1113
526
|
end
|
1114
527
|
end
|
1115
528
|
|
1116
529
|
# The basic building block for constructing lists
|
1117
530
|
#
|
1118
|
-
# A
|
1119
|
-
# the head is an element in the list, and the tail is a reference to the
|
531
|
+
# A Sequence, also known as a "cons cell", has a +head+ and a +tail+, where
|
532
|
+
# the +head+ is an element in the list, and the +tail+ is a reference to the
|
1120
533
|
# rest of the list. This way a singly linked list can be constructed, with
|
1121
|
-
# each
|
1122
|
-
#
|
534
|
+
# each +Sequence+ holding a single element and a pointer to the next
|
535
|
+
# +Sequence+.
|
1123
536
|
#
|
1124
|
-
# The last
|
537
|
+
# The last +Sequence+ instance in the chain has the {EmptyList} as its tail.
|
1125
538
|
#
|
1126
|
-
|
1127
|
-
class Cons
|
539
|
+
class Sequence
|
1128
540
|
include List
|
1129
541
|
|
1130
542
|
attr_reader :head, :tail
|
@@ -1132,292 +544,71 @@ module Hamster
|
|
1132
544
|
def initialize(head, tail = EmptyList)
|
1133
545
|
@head = head
|
1134
546
|
@tail = tail
|
1135
|
-
@size = tail.cached_size? ? tail.size + 1 : nil
|
1136
547
|
end
|
1137
548
|
|
1138
549
|
def empty?
|
1139
550
|
false
|
1140
551
|
end
|
1141
|
-
|
1142
|
-
def size
|
1143
|
-
@size ||= super
|
1144
|
-
end
|
1145
|
-
alias :length :size
|
1146
|
-
|
1147
|
-
def cached_size?
|
1148
|
-
@size != nil
|
1149
|
-
end
|
1150
552
|
end
|
1151
553
|
|
1152
|
-
#
|
1153
|
-
#
|
1154
|
-
#
|
554
|
+
# Lazy list stream
|
555
|
+
#
|
556
|
+
# A +Stream+ takes a block that returns a +List+, i.e. an object that responds
|
557
|
+
# to +head+, +tail+ and +empty?+. The list is only realized when one of these
|
558
|
+
# operations is performed.
|
1155
559
|
#
|
1156
|
-
# By returning a
|
560
|
+
# By returning a +Sequence+ that in turn has a {Stream} as its +tail+, one can
|
1157
561
|
# construct infinite lazy lists.
|
1158
562
|
#
|
1159
|
-
#
|
1160
|
-
class LazyList
|
1161
|
-
include List
|
1162
|
-
|
1163
|
-
def initialize(&block)
|
1164
|
-
@head = block # doubles as storage for block while yet unrealized
|
1165
|
-
@tail = nil
|
1166
|
-
@atomic = Atomic.new(0) # haven't yet run block
|
1167
|
-
@size = nil
|
1168
|
-
end
|
1169
|
-
|
1170
|
-
def head
|
1171
|
-
realize if @atomic.get != 2
|
1172
|
-
@head
|
1173
|
-
end
|
1174
|
-
alias :first :head
|
1175
|
-
|
1176
|
-
def tail
|
1177
|
-
realize if @atomic.get != 2
|
1178
|
-
@tail
|
1179
|
-
end
|
1180
|
-
|
1181
|
-
def empty?
|
1182
|
-
realize if @atomic.get != 2
|
1183
|
-
@size == 0
|
1184
|
-
end
|
1185
|
-
|
1186
|
-
def size
|
1187
|
-
@size ||= super
|
1188
|
-
end
|
1189
|
-
alias :length :size
|
1190
|
-
|
1191
|
-
def cached_size?
|
1192
|
-
@size != nil
|
1193
|
-
end
|
1194
|
-
|
1195
|
-
private
|
1196
|
-
|
1197
|
-
QUEUE = ConditionVariable.new
|
1198
|
-
MUTEX = Mutex.new
|
1199
|
-
|
1200
|
-
def realize
|
1201
|
-
while true
|
1202
|
-
# try to "claim" the right to run the block which realizes target
|
1203
|
-
if @atomic.compare_and_swap(0,1) # full memory barrier here
|
1204
|
-
begin
|
1205
|
-
list = @head.call
|
1206
|
-
if list.empty?
|
1207
|
-
@head, @tail, @size = nil, self, 0
|
1208
|
-
else
|
1209
|
-
@head, @tail = list.head, list.tail
|
1210
|
-
end
|
1211
|
-
rescue
|
1212
|
-
@atomic.set(0)
|
1213
|
-
MUTEX.synchronize { QUEUE.broadcast }
|
1214
|
-
raise
|
1215
|
-
end
|
1216
|
-
@atomic.set(2)
|
1217
|
-
MUTEX.synchronize { QUEUE.broadcast }
|
1218
|
-
return
|
1219
|
-
end
|
1220
|
-
# we failed to "claim" it, another thread must be running it
|
1221
|
-
if @atomic.get == 1 # another thread is running the block
|
1222
|
-
MUTEX.synchronize do
|
1223
|
-
# check value of @atomic again, in case another thread already changed it
|
1224
|
-
# *and* went past the call to QUEUE.broadcast before we got here
|
1225
|
-
QUEUE.wait(MUTEX) if @atomic.get == 1
|
1226
|
-
end
|
1227
|
-
elsif @atomic.get == 2 # another thread finished the block
|
1228
|
-
return
|
1229
|
-
end
|
1230
|
-
end
|
1231
|
-
end
|
1232
|
-
end
|
1233
|
-
|
1234
|
-
# Common behavior for other classes which implement various kinds of lazy lists
|
1235
|
-
# @private
|
1236
|
-
class Realizable
|
1237
|
-
include List
|
1238
|
-
|
1239
|
-
def initialize
|
1240
|
-
@head, @tail, @size = Undefined, Undefined, nil
|
1241
|
-
end
|
1242
|
-
|
1243
|
-
def head
|
1244
|
-
realize if @head == Undefined
|
1245
|
-
@head
|
1246
|
-
end
|
1247
|
-
alias :first :head
|
1248
|
-
|
1249
|
-
def tail
|
1250
|
-
realize if @tail == Undefined
|
1251
|
-
@tail
|
1252
|
-
end
|
1253
|
-
|
1254
|
-
def empty?
|
1255
|
-
realize if @head == Undefined
|
1256
|
-
@size == 0
|
1257
|
-
end
|
1258
|
-
|
1259
|
-
def size
|
1260
|
-
@size ||= super
|
1261
|
-
end
|
1262
|
-
alias :length :size
|
1263
|
-
|
1264
|
-
def cached_size?
|
1265
|
-
@size != nil
|
1266
|
-
end
|
1267
|
-
|
1268
|
-
def realized?
|
1269
|
-
@head != Undefined
|
1270
|
-
end
|
1271
|
-
end
|
1272
|
-
|
1273
|
-
# This class can divide a collection into 2 lazy lists, one of items
|
1274
|
-
# for which the block returns true, and another for false
|
1275
|
-
# At the same time, it guarantees the block will only be called ONCE for each item
|
563
|
+
# The recommended interface for using this is through {Hamster.stream Hamster.stream}
|
1276
564
|
#
|
1277
|
-
|
1278
|
-
|
1279
|
-
attr_reader :left, :right
|
1280
|
-
def initialize(list, block)
|
1281
|
-
@list, @block, @left, @right = list, block, [], []
|
1282
|
-
end
|
1283
|
-
|
1284
|
-
def next_item
|
1285
|
-
unless @list.empty?
|
1286
|
-
item = @list.head
|
1287
|
-
(@block.call(item) ? @left : @right) << item
|
1288
|
-
@list = @list.tail
|
1289
|
-
end
|
1290
|
-
end
|
565
|
+
class Stream
|
566
|
+
extend Forwardable
|
1291
567
|
|
1292
|
-
|
1293
|
-
@list.empty?
|
1294
|
-
end
|
1295
|
-
end
|
1296
|
-
|
1297
|
-
# One of the lazy lists which gets its items from a Partitioner
|
1298
|
-
# @private
|
1299
|
-
class Partitioned < Realizable
|
1300
|
-
def initialize(partitioner, buffer, mutex)
|
1301
|
-
super()
|
1302
|
-
@partitioner, @buffer, @mutex = partitioner, buffer, mutex
|
1303
|
-
end
|
1304
|
-
|
1305
|
-
def realize
|
1306
|
-
@mutex.synchronize do
|
1307
|
-
return if @head != Undefined # another thread got ahead of us
|
1308
|
-
while true
|
1309
|
-
if !@buffer.empty?
|
1310
|
-
@head = @buffer.shift
|
1311
|
-
@tail = Partitioned.new(@partitioner, @buffer, @mutex)
|
1312
|
-
# don't hold onto references
|
1313
|
-
# tail will keep references alive until end of list is reached
|
1314
|
-
@partitioner, @buffer, @mutex = nil, nil, nil
|
1315
|
-
return
|
1316
|
-
elsif @partitioner.done?
|
1317
|
-
@head, @size, @tail = nil, 0, self
|
1318
|
-
@partitioner, @buffer, @mutex = nil, nil, nil # allow them to be GC'd
|
1319
|
-
return
|
1320
|
-
else
|
1321
|
-
@partitioner.next_item
|
1322
|
-
end
|
1323
|
-
end
|
1324
|
-
end
|
1325
|
-
end
|
1326
|
-
end
|
568
|
+
include List
|
1327
569
|
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
#
|
1332
|
-
# @private
|
1333
|
-
class Splitter
|
1334
|
-
attr_reader :left, :right
|
1335
|
-
def initialize(list, block)
|
1336
|
-
@list, @block, @left, @right = list, block, [], EmptyList
|
1337
|
-
end
|
1338
|
-
|
1339
|
-
def next_item
|
1340
|
-
unless @list.empty?
|
1341
|
-
item = @list.head
|
1342
|
-
if @block.call(item)
|
1343
|
-
@left << item
|
1344
|
-
@list = @list.tail
|
1345
|
-
else
|
1346
|
-
@right = @list
|
1347
|
-
@list = EmptyList
|
1348
|
-
end
|
1349
|
-
end
|
570
|
+
def initialize(&block)
|
571
|
+
@block = block
|
572
|
+
@lock = Mutex.new
|
1350
573
|
end
|
1351
574
|
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
575
|
+
def_delegator :target, :head
|
576
|
+
def_delegator :target, :tail
|
577
|
+
def_delegator :target, :empty?
|
1355
578
|
|
1356
|
-
|
1357
|
-
class Left < Realizable
|
1358
|
-
def initialize(splitter, buffer, mutex)
|
1359
|
-
super()
|
1360
|
-
@splitter, @buffer, @mutex = splitter, buffer, mutex
|
1361
|
-
end
|
579
|
+
protected
|
1362
580
|
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
@head = @buffer.shift
|
1369
|
-
@tail = Left.new(@splitter, @buffer, @mutex)
|
1370
|
-
@splitter, @buffer, @mutex = nil, nil, nil
|
1371
|
-
return
|
1372
|
-
elsif @splitter.done?
|
1373
|
-
@head, @size, @tail = nil, 0, self
|
1374
|
-
@splitter, @buffer, @mutex = nil, nil, nil
|
1375
|
-
return
|
1376
|
-
else
|
1377
|
-
@splitter.next_item
|
1378
|
-
end
|
1379
|
-
end
|
581
|
+
def vivify
|
582
|
+
@lock.synchronize do
|
583
|
+
unless @block.nil?
|
584
|
+
@target = @block.call
|
585
|
+
@block = nil
|
1380
586
|
end
|
1381
587
|
end
|
588
|
+
@target
|
1382
589
|
end
|
1383
590
|
|
1384
|
-
|
1385
|
-
class Right < Realizable
|
1386
|
-
def initialize(splitter, mutex)
|
1387
|
-
super()
|
1388
|
-
@splitter, @mutex = splitter, mutex
|
1389
|
-
end
|
591
|
+
private
|
1390
592
|
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
if @splitter.right.empty?
|
1396
|
-
@head, @size, @tail = nil, 0, self
|
1397
|
-
else
|
1398
|
-
@head, @tail = @splitter.right.head, @splitter.right.tail
|
1399
|
-
end
|
1400
|
-
@splitter, @mutex = nil, nil
|
1401
|
-
end
|
1402
|
-
end
|
593
|
+
def target
|
594
|
+
list = vivify
|
595
|
+
list = list.vivify while list.is_a?(Stream)
|
596
|
+
list
|
1403
597
|
end
|
1404
598
|
end
|
1405
599
|
|
1406
|
-
# A list without any elements
|
600
|
+
# A list without any elements
|
1407
601
|
#
|
602
|
+
# This is a singleton, since all empty lists are equivalent. It is used
|
603
|
+
# as a terminating element in a chain of +Sequence+ instances.
|
1408
604
|
module EmptyList
|
1409
605
|
class << self
|
1410
606
|
include List
|
1411
607
|
|
1412
|
-
# There is no first item in an empty list, so return `nil`.
|
1413
|
-
# @return [nil]
|
1414
608
|
def head
|
1415
609
|
nil
|
1416
610
|
end
|
1417
|
-
alias :first :head
|
1418
611
|
|
1419
|
-
# There are no subsequent elements, so return an empty list.
|
1420
|
-
# @return [self]
|
1421
612
|
def tail
|
1422
613
|
self
|
1423
614
|
end
|
@@ -1425,17 +616,6 @@ module Hamster
|
|
1425
616
|
def empty?
|
1426
617
|
true
|
1427
618
|
end
|
1428
|
-
|
1429
|
-
# Return the number of items in this `List`.
|
1430
|
-
# @return [Integer]
|
1431
|
-
def size
|
1432
|
-
0
|
1433
|
-
end
|
1434
|
-
alias :length :size
|
1435
|
-
|
1436
|
-
def cached_size?
|
1437
|
-
true
|
1438
|
-
end
|
1439
619
|
end
|
1440
620
|
end
|
1441
621
|
end
|