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