immutable-ruby 0.0.4 → 0.2.0
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/immutable/_core.rb +3067 -0
- data/lib/immutable/core_ext/enumerable.rb +1 -1
- data/lib/immutable/core_ext/io.rb +1 -1
- data/lib/immutable/core_ext.rb +2 -2
- data/lib/immutable/deque.rb +17 -17
- data/lib/immutable/enumerable.rb +10 -10
- data/lib/immutable/hash.rb +4 -941
- data/lib/immutable/list.rb +36 -36
- data/lib/immutable/nested.rb +8 -8
- data/lib/immutable/set.rb +3 -583
- data/lib/immutable/sorted_set.rb +29 -27
- data/lib/immutable/trie.rb +2 -2
- data/lib/immutable/vector.rb +3 -1549
- data/lib/immutable/version.rb +1 -1
- data/lib/immutable.rb +9 -9
- metadata +34 -696
- data/lib/immutable/core_ext/struct.rb +0 -9
- data/spec/fixtures/io_spec.txt +0 -3
- data/spec/lib/immutable/core_ext/array_spec.rb +0 -13
- data/spec/lib/immutable/core_ext/enumerable_spec.rb +0 -29
- data/spec/lib/immutable/core_ext/io_spec.rb +0 -28
- data/spec/lib/immutable/deque/clear_spec.rb +0 -33
- data/spec/lib/immutable/deque/construction_spec.rb +0 -29
- data/spec/lib/immutable/deque/copying_spec.rb +0 -19
- data/spec/lib/immutable/deque/dequeue_spec.rb +0 -34
- data/spec/lib/immutable/deque/empty_spec.rb +0 -39
- data/spec/lib/immutable/deque/enqueue_spec.rb +0 -27
- data/spec/lib/immutable/deque/first_spec.rb +0 -17
- data/spec/lib/immutable/deque/inspect_spec.rb +0 -23
- data/spec/lib/immutable/deque/last_spec.rb +0 -17
- data/spec/lib/immutable/deque/marshal_spec.rb +0 -33
- data/spec/lib/immutable/deque/new_spec.rb +0 -43
- data/spec/lib/immutable/deque/pop_spec.rb +0 -36
- data/spec/lib/immutable/deque/pretty_print_spec.rb +0 -23
- data/spec/lib/immutable/deque/push_spec.rb +0 -36
- data/spec/lib/immutable/deque/random_modification_spec.rb +0 -33
- data/spec/lib/immutable/deque/rotate_spec.rb +0 -68
- data/spec/lib/immutable/deque/shift_spec.rb +0 -29
- data/spec/lib/immutable/deque/size_spec.rb +0 -19
- data/spec/lib/immutable/deque/to_a_spec.rb +0 -26
- data/spec/lib/immutable/deque/to_ary_spec.rb +0 -35
- data/spec/lib/immutable/deque/to_list_spec.rb +0 -24
- data/spec/lib/immutable/deque/unshift_spec.rb +0 -30
- data/spec/lib/immutable/hash/all_spec.rb +0 -53
- data/spec/lib/immutable/hash/any_spec.rb +0 -53
- data/spec/lib/immutable/hash/assoc_spec.rb +0 -51
- data/spec/lib/immutable/hash/clear_spec.rb +0 -42
- data/spec/lib/immutable/hash/construction_spec.rb +0 -38
- data/spec/lib/immutable/hash/copying_spec.rb +0 -13
- data/spec/lib/immutable/hash/default_proc_spec.rb +0 -72
- data/spec/lib/immutable/hash/delete_spec.rb +0 -39
- data/spec/lib/immutable/hash/dig_spec.rb +0 -34
- data/spec/lib/immutable/hash/each_spec.rb +0 -77
- data/spec/lib/immutable/hash/each_with_index_spec.rb +0 -29
- data/spec/lib/immutable/hash/empty_spec.rb +0 -43
- data/spec/lib/immutable/hash/eql_spec.rb +0 -75
- data/spec/lib/immutable/hash/except_spec.rb +0 -42
- data/spec/lib/immutable/hash/fetch_spec.rb +0 -57
- data/spec/lib/immutable/hash/fetch_values_spec.rb +0 -22
- data/spec/lib/immutable/hash/find_spec.rb +0 -43
- data/spec/lib/immutable/hash/flat_map_spec.rb +0 -35
- data/spec/lib/immutable/hash/flatten_spec.rb +0 -98
- data/spec/lib/immutable/hash/get_spec.rb +0 -79
- data/spec/lib/immutable/hash/has_key_spec.rb +0 -31
- data/spec/lib/immutable/hash/has_value_spec.rb +0 -27
- data/spec/lib/immutable/hash/hash_spec.rb +0 -29
- data/spec/lib/immutable/hash/inspect_spec.rb +0 -30
- data/spec/lib/immutable/hash/invert_spec.rb +0 -30
- data/spec/lib/immutable/hash/key_spec.rb +0 -27
- data/spec/lib/immutable/hash/keys_spec.rb +0 -15
- data/spec/lib/immutable/hash/map_spec.rb +0 -45
- data/spec/lib/immutable/hash/marshal_spec.rb +0 -28
- data/spec/lib/immutable/hash/merge_spec.rb +0 -82
- data/spec/lib/immutable/hash/min_max_spec.rb +0 -45
- data/spec/lib/immutable/hash/new_spec.rb +0 -70
- data/spec/lib/immutable/hash/none_spec.rb +0 -48
- data/spec/lib/immutable/hash/partition_spec.rb +0 -35
- data/spec/lib/immutable/hash/pretty_print_spec.rb +0 -34
- data/spec/lib/immutable/hash/put_spec.rb +0 -111
- data/spec/lib/immutable/hash/reduce_spec.rb +0 -35
- data/spec/lib/immutable/hash/reject_spec.rb +0 -61
- data/spec/lib/immutable/hash/reverse_each_spec.rb +0 -27
- data/spec/lib/immutable/hash/sample_spec.rb +0 -13
- data/spec/lib/immutable/hash/select_spec.rb +0 -57
- data/spec/lib/immutable/hash/size_spec.rb +0 -51
- data/spec/lib/immutable/hash/slice_spec.rb +0 -44
- data/spec/lib/immutable/hash/sort_spec.rb +0 -26
- data/spec/lib/immutable/hash/store_spec.rb +0 -75
- data/spec/lib/immutable/hash/subset_spec.rb +0 -42
- data/spec/lib/immutable/hash/superset_spec.rb +0 -42
- data/spec/lib/immutable/hash/take_spec.rb +0 -35
- data/spec/lib/immutable/hash/to_a_spec.rb +0 -13
- data/spec/lib/immutable/hash/to_hash_spec.rb +0 -21
- data/spec/lib/immutable/hash/to_proc_spec.rb +0 -39
- data/spec/lib/immutable/hash/update_in_spec.rb +0 -79
- data/spec/lib/immutable/hash/values_at_spec.rb +0 -33
- data/spec/lib/immutable/hash/values_spec.rb +0 -23
- data/spec/lib/immutable/list/add_spec.rb +0 -25
- data/spec/lib/immutable/list/all_spec.rb +0 -57
- data/spec/lib/immutable/list/any_spec.rb +0 -49
- data/spec/lib/immutable/list/append_spec.rb +0 -38
- data/spec/lib/immutable/list/at_spec.rb +0 -29
- data/spec/lib/immutable/list/break_spec.rb +0 -69
- data/spec/lib/immutable/list/cadr_spec.rb +0 -38
- data/spec/lib/immutable/list/chunk_spec.rb +0 -28
- data/spec/lib/immutable/list/clear_spec.rb +0 -24
- data/spec/lib/immutable/list/combination_spec.rb +0 -33
- data/spec/lib/immutable/list/compact_spec.rb +0 -34
- data/spec/lib/immutable/list/compare_spec.rb +0 -30
- data/spec/lib/immutable/list/cons_spec.rb +0 -25
- data/spec/lib/immutable/list/construction_spec.rb +0 -110
- data/spec/lib/immutable/list/copying_spec.rb +0 -19
- data/spec/lib/immutable/list/count_spec.rb +0 -36
- data/spec/lib/immutable/list/cycle_spec.rb +0 -28
- data/spec/lib/immutable/list/delete_at_spec.rb +0 -18
- data/spec/lib/immutable/list/delete_spec.rb +0 -16
- data/spec/lib/immutable/list/drop_spec.rb +0 -30
- data/spec/lib/immutable/list/drop_while_spec.rb +0 -38
- data/spec/lib/immutable/list/each_slice_spec.rb +0 -51
- data/spec/lib/immutable/list/each_spec.rb +0 -40
- data/spec/lib/immutable/list/each_with_index_spec.rb +0 -28
- data/spec/lib/immutable/list/empty_spec.rb +0 -23
- data/spec/lib/immutable/list/eql_spec.rb +0 -61
- data/spec/lib/immutable/list/fill_spec.rb +0 -49
- data/spec/lib/immutable/list/find_all_spec.rb +0 -70
- data/spec/lib/immutable/list/find_index_spec.rb +0 -35
- data/spec/lib/immutable/list/find_spec.rb +0 -42
- data/spec/lib/immutable/list/flat_map_spec.rb +0 -51
- data/spec/lib/immutable/list/flatten_spec.rb +0 -30
- data/spec/lib/immutable/list/grep_spec.rb +0 -46
- data/spec/lib/immutable/list/group_by_spec.rb +0 -41
- data/spec/lib/immutable/list/hash_spec.rb +0 -21
- data/spec/lib/immutable/list/head_spec.rb +0 -19
- data/spec/lib/immutable/list/include_spec.rb +0 -35
- data/spec/lib/immutable/list/index_spec.rb +0 -37
- data/spec/lib/immutable/list/indices_spec.rb +0 -61
- data/spec/lib/immutable/list/init_spec.rb +0 -28
- data/spec/lib/immutable/list/inits_spec.rb +0 -28
- data/spec/lib/immutable/list/insert_spec.rb +0 -46
- data/spec/lib/immutable/list/inspect_spec.rb +0 -29
- data/spec/lib/immutable/list/intersperse_spec.rb +0 -28
- data/spec/lib/immutable/list/join_spec.rb +0 -63
- data/spec/lib/immutable/list/last_spec.rb +0 -23
- data/spec/lib/immutable/list/ltlt_spec.rb +0 -19
- data/spec/lib/immutable/list/map_spec.rb +0 -45
- data/spec/lib/immutable/list/maximum_spec.rb +0 -39
- data/spec/lib/immutable/list/merge_by_spec.rb +0 -51
- data/spec/lib/immutable/list/merge_spec.rb +0 -59
- data/spec/lib/immutable/list/minimum_spec.rb +0 -39
- data/spec/lib/immutable/list/multithreading_spec.rb +0 -47
- data/spec/lib/immutable/list/none_spec.rb +0 -47
- data/spec/lib/immutable/list/one_spec.rb +0 -49
- data/spec/lib/immutable/list/partition_spec.rb +0 -115
- data/spec/lib/immutable/list/permutation_spec.rb +0 -55
- data/spec/lib/immutable/list/pop_spec.rb +0 -25
- data/spec/lib/immutable/list/product_spec.rb +0 -23
- data/spec/lib/immutable/list/reduce_spec.rb +0 -53
- data/spec/lib/immutable/list/reject_spec.rb +0 -45
- data/spec/lib/immutable/list/reverse_spec.rb +0 -34
- data/spec/lib/immutable/list/rotate_spec.rb +0 -36
- data/spec/lib/immutable/list/sample_spec.rb +0 -13
- data/spec/lib/immutable/list/select_spec.rb +0 -70
- data/spec/lib/immutable/list/size_spec.rb +0 -25
- data/spec/lib/immutable/list/slice_spec.rb +0 -229
- data/spec/lib/immutable/list/sorting_spec.rb +0 -46
- data/spec/lib/immutable/list/span_spec.rb +0 -76
- data/spec/lib/immutable/list/split_at_spec.rb +0 -43
- data/spec/lib/immutable/list/subsequences_spec.rb +0 -23
- data/spec/lib/immutable/list/sum_spec.rb +0 -23
- data/spec/lib/immutable/list/tail_spec.rb +0 -30
- data/spec/lib/immutable/list/tails_spec.rb +0 -28
- data/spec/lib/immutable/list/take_spec.rb +0 -30
- data/spec/lib/immutable/list/take_while_spec.rb +0 -46
- data/spec/lib/immutable/list/to_a_spec.rb +0 -39
- data/spec/lib/immutable/list/to_ary_spec.rb +0 -41
- data/spec/lib/immutable/list/to_list_spec.rb +0 -19
- data/spec/lib/immutable/list/to_set_spec.rb +0 -17
- data/spec/lib/immutable/list/transpose_spec.rb +0 -19
- data/spec/lib/immutable/list/union_spec.rb +0 -31
- data/spec/lib/immutable/list/uniq_spec.rb +0 -35
- data/spec/lib/immutable/list/zip_spec.rb +0 -23
- data/spec/lib/immutable/nested/construction_spec.rb +0 -101
- data/spec/lib/immutable/set/add_spec.rb +0 -77
- data/spec/lib/immutable/set/all_spec.rb +0 -51
- data/spec/lib/immutable/set/any_spec.rb +0 -51
- data/spec/lib/immutable/set/clear_spec.rb +0 -33
- data/spec/lib/immutable/set/compact_spec.rb +0 -30
- data/spec/lib/immutable/set/construction_spec.rb +0 -18
- data/spec/lib/immutable/set/copying_spec.rb +0 -13
- data/spec/lib/immutable/set/count_spec.rb +0 -36
- data/spec/lib/immutable/set/delete_spec.rb +0 -71
- data/spec/lib/immutable/set/difference_spec.rb +0 -49
- data/spec/lib/immutable/set/disjoint_spec.rb +0 -25
- data/spec/lib/immutable/set/each_spec.rb +0 -45
- data/spec/lib/immutable/set/empty_spec.rb +0 -44
- data/spec/lib/immutable/set/eqeq_spec.rb +0 -103
- data/spec/lib/immutable/set/eql_spec.rb +0 -109
- data/spec/lib/immutable/set/exclusion_spec.rb +0 -47
- data/spec/lib/immutable/set/find_spec.rb +0 -35
- data/spec/lib/immutable/set/first_spec.rb +0 -28
- data/spec/lib/immutable/set/flatten_spec.rb +0 -46
- data/spec/lib/immutable/set/grep_spec.rb +0 -57
- data/spec/lib/immutable/set/grep_v_spec.rb +0 -59
- data/spec/lib/immutable/set/group_by_spec.rb +0 -59
- data/spec/lib/immutable/set/hash_spec.rb +0 -22
- data/spec/lib/immutable/set/include_spec.rb +0 -60
- data/spec/lib/immutable/set/inspect_spec.rb +0 -47
- data/spec/lib/immutable/set/intersect_spec.rb +0 -25
- data/spec/lib/immutable/set/intersection_spec.rb +0 -52
- data/spec/lib/immutable/set/join_spec.rb +0 -64
- data/spec/lib/immutable/set/map_spec.rb +0 -59
- data/spec/lib/immutable/set/marshal_spec.rb +0 -28
- data/spec/lib/immutable/set/maximum_spec.rb +0 -36
- data/spec/lib/immutable/set/minimum_spec.rb +0 -36
- data/spec/lib/immutable/set/new_spec.rb +0 -53
- data/spec/lib/immutable/set/none_spec.rb +0 -47
- data/spec/lib/immutable/set/one_spec.rb +0 -47
- data/spec/lib/immutable/set/partition_spec.rb +0 -52
- data/spec/lib/immutable/set/product_spec.rb +0 -23
- data/spec/lib/immutable/set/reduce_spec.rb +0 -55
- data/spec/lib/immutable/set/reject_spec.rb +0 -50
- data/spec/lib/immutable/set/reverse_each_spec.rb +0 -38
- data/spec/lib/immutable/set/sample_spec.rb +0 -13
- data/spec/lib/immutable/set/select_spec.rb +0 -73
- data/spec/lib/immutable/set/size_spec.rb +0 -17
- data/spec/lib/immutable/set/sorting_spec.rb +0 -65
- data/spec/lib/immutable/set/subset_spec.rb +0 -51
- data/spec/lib/immutable/set/sum_spec.rb +0 -23
- data/spec/lib/immutable/set/superset_spec.rb +0 -51
- data/spec/lib/immutable/set/to_a_spec.rb +0 -30
- data/spec/lib/immutable/set/to_list_spec.rb +0 -35
- data/spec/lib/immutable/set/to_set_spec.rb +0 -19
- data/spec/lib/immutable/set/union_spec.rb +0 -63
- data/spec/lib/immutable/sorted_set/above_spec.rb +0 -51
- data/spec/lib/immutable/sorted_set/add_spec.rb +0 -62
- data/spec/lib/immutable/sorted_set/at_spec.rb +0 -24
- data/spec/lib/immutable/sorted_set/below_spec.rb +0 -51
- data/spec/lib/immutable/sorted_set/between_spec.rb +0 -51
- data/spec/lib/immutable/sorted_set/clear_spec.rb +0 -43
- data/spec/lib/immutable/sorted_set/copying_spec.rb +0 -20
- data/spec/lib/immutable/sorted_set/delete_at_spec.rb +0 -18
- data/spec/lib/immutable/sorted_set/delete_spec.rb +0 -89
- data/spec/lib/immutable/sorted_set/difference_spec.rb +0 -22
- data/spec/lib/immutable/sorted_set/disjoint_spec.rb +0 -25
- data/spec/lib/immutable/sorted_set/drop_spec.rb +0 -55
- data/spec/lib/immutable/sorted_set/drop_while_spec.rb +0 -34
- data/spec/lib/immutable/sorted_set/each_spec.rb +0 -28
- data/spec/lib/immutable/sorted_set/empty_spec.rb +0 -34
- data/spec/lib/immutable/sorted_set/eql_spec.rb +0 -120
- data/spec/lib/immutable/sorted_set/exclusion_spec.rb +0 -22
- data/spec/lib/immutable/sorted_set/fetch_spec.rb +0 -64
- data/spec/lib/immutable/sorted_set/find_index_spec.rb +0 -40
- data/spec/lib/immutable/sorted_set/first_spec.rb +0 -18
- data/spec/lib/immutable/sorted_set/from_spec.rb +0 -51
- data/spec/lib/immutable/sorted_set/group_by_spec.rb +0 -57
- data/spec/lib/immutable/sorted_set/include_spec.rb +0 -23
- data/spec/lib/immutable/sorted_set/inspect_spec.rb +0 -37
- data/spec/lib/immutable/sorted_set/intersect_spec.rb +0 -25
- data/spec/lib/immutable/sorted_set/intersection_spec.rb +0 -28
- data/spec/lib/immutable/sorted_set/last_spec.rb +0 -36
- data/spec/lib/immutable/sorted_set/map_spec.rb +0 -51
- data/spec/lib/immutable/sorted_set/marshal_spec.rb +0 -36
- data/spec/lib/immutable/sorted_set/maximum_spec.rb +0 -36
- data/spec/lib/immutable/sorted_set/minimum_spec.rb +0 -19
- data/spec/lib/immutable/sorted_set/new_spec.rb +0 -137
- data/spec/lib/immutable/sorted_set/reverse_each_spec.rb +0 -28
- data/spec/lib/immutable/sorted_set/sample_spec.rb +0 -13
- data/spec/lib/immutable/sorted_set/select_spec.rb +0 -61
- data/spec/lib/immutable/sorted_set/size_spec.rb +0 -17
- data/spec/lib/immutable/sorted_set/slice_spec.rb +0 -256
- data/spec/lib/immutable/sorted_set/sorting_spec.rb +0 -56
- data/spec/lib/immutable/sorted_set/subset_spec.rb +0 -47
- data/spec/lib/immutable/sorted_set/superset_spec.rb +0 -47
- data/spec/lib/immutable/sorted_set/take_spec.rb +0 -54
- data/spec/lib/immutable/sorted_set/take_while_spec.rb +0 -33
- data/spec/lib/immutable/sorted_set/to_set_spec.rb +0 -17
- data/spec/lib/immutable/sorted_set/union_spec.rb +0 -58
- data/spec/lib/immutable/sorted_set/up_to_spec.rb +0 -52
- data/spec/lib/immutable/sorted_set/util_spec.rb +0 -48
- data/spec/lib/immutable/sorted_set/values_at_spec.rb +0 -33
- data/spec/lib/immutable/vector/add_spec.rb +0 -67
- data/spec/lib/immutable/vector/any_spec.rb +0 -69
- data/spec/lib/immutable/vector/assoc_spec.rb +0 -45
- data/spec/lib/immutable/vector/bsearch_spec.rb +0 -65
- data/spec/lib/immutable/vector/clear_spec.rb +0 -33
- data/spec/lib/immutable/vector/combination_spec.rb +0 -81
- data/spec/lib/immutable/vector/compact_spec.rb +0 -29
- data/spec/lib/immutable/vector/compare_spec.rb +0 -31
- data/spec/lib/immutable/vector/concat_spec.rb +0 -34
- data/spec/lib/immutable/vector/copying_spec.rb +0 -20
- data/spec/lib/immutable/vector/count_spec.rb +0 -17
- data/spec/lib/immutable/vector/delete_at_spec.rb +0 -53
- data/spec/lib/immutable/vector/delete_spec.rb +0 -30
- data/spec/lib/immutable/vector/dig_spec.rb +0 -30
- data/spec/lib/immutable/vector/drop_spec.rb +0 -41
- data/spec/lib/immutable/vector/drop_while_spec.rb +0 -54
- data/spec/lib/immutable/vector/each_index_spec.rb +0 -40
- data/spec/lib/immutable/vector/each_spec.rb +0 -44
- data/spec/lib/immutable/vector/each_with_index_spec.rb +0 -39
- data/spec/lib/immutable/vector/empty_spec.rb +0 -41
- data/spec/lib/immutable/vector/eql_spec.rb +0 -76
- data/spec/lib/immutable/vector/fetch_spec.rb +0 -64
- data/spec/lib/immutable/vector/fill_spec.rb +0 -88
- data/spec/lib/immutable/vector/first_spec.rb +0 -18
- data/spec/lib/immutable/vector/flat_map_spec.rb +0 -50
- data/spec/lib/immutable/vector/flatten_spec.rb +0 -58
- data/spec/lib/immutable/vector/get_spec.rb +0 -74
- data/spec/lib/immutable/vector/group_by_spec.rb +0 -57
- data/spec/lib/immutable/vector/include_spec.rb +0 -30
- data/spec/lib/immutable/vector/insert_spec.rb +0 -68
- data/spec/lib/immutable/vector/inspect_spec.rb +0 -49
- data/spec/lib/immutable/vector/join_spec.rb +0 -58
- data/spec/lib/immutable/vector/last_spec.rb +0 -45
- data/spec/lib/immutable/vector/length_spec.rb +0 -45
- data/spec/lib/immutable/vector/ltlt_spec.rb +0 -65
- data/spec/lib/immutable/vector/map_spec.rb +0 -51
- data/spec/lib/immutable/vector/marshal_spec.rb +0 -31
- data/spec/lib/immutable/vector/maximum_spec.rb +0 -33
- data/spec/lib/immutable/vector/minimum_spec.rb +0 -33
- data/spec/lib/immutable/vector/multiply_spec.rb +0 -47
- data/spec/lib/immutable/vector/new_spec.rb +0 -50
- data/spec/lib/immutable/vector/partition_spec.rb +0 -52
- data/spec/lib/immutable/vector/permutation_spec.rb +0 -91
- data/spec/lib/immutable/vector/pop_spec.rb +0 -26
- data/spec/lib/immutable/vector/product_spec.rb +0 -70
- data/spec/lib/immutable/vector/reduce_spec.rb +0 -55
- data/spec/lib/immutable/vector/reject_spec.rb +0 -43
- data/spec/lib/immutable/vector/repeated_combination_spec.rb +0 -77
- data/spec/lib/immutable/vector/repeated_permutation_spec.rb +0 -93
- data/spec/lib/immutable/vector/reverse_each_spec.rb +0 -31
- data/spec/lib/immutable/vector/reverse_spec.rb +0 -21
- data/spec/lib/immutable/vector/rindex_spec.rb +0 -36
- data/spec/lib/immutable/vector/rotate_spec.rb +0 -73
- data/spec/lib/immutable/vector/sample_spec.rb +0 -13
- data/spec/lib/immutable/vector/select_spec.rb +0 -63
- data/spec/lib/immutable/vector/set_spec.rb +0 -174
- data/spec/lib/immutable/vector/shift_spec.rb +0 -27
- data/spec/lib/immutable/vector/shuffle_spec.rb +0 -43
- data/spec/lib/immutable/vector/slice_spec.rb +0 -240
- data/spec/lib/immutable/vector/sorting_spec.rb +0 -56
- data/spec/lib/immutable/vector/sum_spec.rb +0 -17
- data/spec/lib/immutable/vector/take_spec.rb +0 -42
- data/spec/lib/immutable/vector/take_while_spec.rb +0 -34
- data/spec/lib/immutable/vector/to_a_spec.rb +0 -41
- data/spec/lib/immutable/vector/to_ary_spec.rb +0 -34
- data/spec/lib/immutable/vector/to_list_spec.rb +0 -30
- data/spec/lib/immutable/vector/to_set_spec.rb +0 -21
- data/spec/lib/immutable/vector/transpose_spec.rb +0 -48
- data/spec/lib/immutable/vector/uniq_spec.rb +0 -76
- data/spec/lib/immutable/vector/unshift_spec.rb +0 -28
- data/spec/lib/immutable/vector/update_in_spec.rb +0 -82
- data/spec/lib/immutable/vector/values_at_spec.rb +0 -33
- data/spec/lib/immutable/vector/zip_spec.rb +0 -57
- data/spec/lib/load_spec.rb +0 -42
- data/spec/spec_helper.rb +0 -96
@@ -0,0 +1,3067 @@
|
|
1
|
+
require 'immutable/undefined'
|
2
|
+
require 'immutable/enumerable'
|
3
|
+
require 'immutable/trie'
|
4
|
+
require 'immutable/sorted_set'
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
module Immutable
|
8
|
+
|
9
|
+
# An `Immutable::Hash` maps a set of unique keys to corresponding values, much
|
10
|
+
# like a dictionary maps from words to definitions. Given a key, it can store
|
11
|
+
# and retrieve an associated value in constant time. If an existing key is
|
12
|
+
# stored again, the new value will replace the old. It behaves much like
|
13
|
+
# Ruby's built-in Hash, which we will call RubyHash for clarity. Like
|
14
|
+
# RubyHash, two keys that are `#eql?` to each other and have the same
|
15
|
+
# `#hash` are considered identical in an `Immutable::Hash`.
|
16
|
+
#
|
17
|
+
# An `Immutable::Hash` can be created in a couple of ways:
|
18
|
+
#
|
19
|
+
# Immutable::Hash.new(font_size: 10, font_family: 'Arial')
|
20
|
+
# Immutable::Hash[first_name: 'John', last_name: 'Smith']
|
21
|
+
#
|
22
|
+
# Any `Enumerable` object which yields two-element `[key, value]` arrays
|
23
|
+
# can be used to initialize an `Immutable::Hash`:
|
24
|
+
#
|
25
|
+
# Immutable::Hash.new([[:first_name, 'John'], [:last_name, 'Smith']])
|
26
|
+
#
|
27
|
+
# Key/value pairs can be added using {#put}. A new hash is returned and the
|
28
|
+
# existing one is left unchanged:
|
29
|
+
#
|
30
|
+
# hash = Immutable::Hash[a: 100, b: 200]
|
31
|
+
# hash.put(:c, 500) # => Immutable::Hash[:a => 100, :b => 200, :c => 500]
|
32
|
+
# hash # => Immutable::Hash[:a => 100, :b => 200]
|
33
|
+
#
|
34
|
+
# {#put} can also take a block, which is used to calculate the value to be
|
35
|
+
# stored.
|
36
|
+
#
|
37
|
+
# hash.put(:a) { |current| current + 200 } # => Immutable::Hash[:a => 300, :b => 200]
|
38
|
+
#
|
39
|
+
# Since it is immutable, all methods which you might expect to "modify" a
|
40
|
+
# `Immutable::Hash` actually return a new hash and leave the existing one
|
41
|
+
# unchanged. This means that the `hash[key] = value` syntax from RubyHash
|
42
|
+
# *cannot* be used with `Immutable::Hash`.
|
43
|
+
#
|
44
|
+
# Nested data structures can easily be updated using {#update_in}:
|
45
|
+
#
|
46
|
+
# hash = Immutable::Hash["a" => Immutable::Vector[Immutable::Hash["c" => 42]]]
|
47
|
+
# hash.update_in("a", 0, "c") { |value| value + 5 }
|
48
|
+
# # => Immutable::Hash["a" => Immutable::Hash["b" => Immutable::Hash["c" => 47]]]
|
49
|
+
#
|
50
|
+
# While an `Immutable::Hash` can iterate over its keys or values, it does not
|
51
|
+
# guarantee any specific iteration order (unlike RubyHash). Methods like
|
52
|
+
# {#flatten} do not guarantee the order of returned key/value pairs.
|
53
|
+
#
|
54
|
+
# Like RubyHash, an `Immutable::Hash` can have a default block which is used
|
55
|
+
# when looking up a key that does not exist. Unlike RubyHash, the default
|
56
|
+
# block will only be passed the missing key, without the hash itself:
|
57
|
+
#
|
58
|
+
# hash = Immutable::Hash.new { |missing_key| missing_key * 10 }
|
59
|
+
# hash[5] # => 50
|
60
|
+
class Hash
|
61
|
+
include Immutable::Enumerable
|
62
|
+
|
63
|
+
class << self
|
64
|
+
# Create a new `Hash` populated with the given key/value pairs.
|
65
|
+
#
|
66
|
+
# @example
|
67
|
+
# Immutable::Hash["A" => 1, "B" => 2] # => Immutable::Hash["A" => 1, "B" => 2]
|
68
|
+
# Immutable::Hash[["A", 1], ["B", 2]] # => Immutable::Hash["A" => 1, "B" => 2]
|
69
|
+
#
|
70
|
+
# @param pairs [::Enumerable] initial content of hash. An empty hash is returned if not provided.
|
71
|
+
# @return [Hash]
|
72
|
+
def [](pairs = nil)
|
73
|
+
(pairs.nil? || pairs.empty?) ? empty : new(pairs)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return an empty `Hash`. If used on a subclass, returns an empty instance
|
77
|
+
# of that class.
|
78
|
+
#
|
79
|
+
# @return [Hash]
|
80
|
+
def empty
|
81
|
+
@empty ||= new
|
82
|
+
end
|
83
|
+
|
84
|
+
# "Raw" allocation of a new `Hash`. Used internally to create a new
|
85
|
+
# instance quickly after obtaining a modified {Trie}.
|
86
|
+
#
|
87
|
+
# @return [Hash]
|
88
|
+
# @private
|
89
|
+
def alloc(trie = EmptyTrie, block = nil)
|
90
|
+
obj = allocate
|
91
|
+
obj.instance_variable_set(:@trie, trie)
|
92
|
+
obj.instance_variable_set(:@default, block)
|
93
|
+
obj.freeze
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# @param pairs [::Enumerable] initial content of hash. An empty hash is returned if not provided.
|
98
|
+
# @yield [key] Optional _default block_ to be stored and used to calculate the default value of a missing key. It will not be yielded during this method. It will not be preserved when marshalling.
|
99
|
+
# @yieldparam key Key that was not present in the hash.
|
100
|
+
def initialize(pairs = nil, &block)
|
101
|
+
@trie = pairs ? Trie[pairs] : EmptyTrie
|
102
|
+
@default = block
|
103
|
+
freeze
|
104
|
+
end
|
105
|
+
|
106
|
+
# Return the default block if there is one. Otherwise, return `nil`.
|
107
|
+
#
|
108
|
+
# @return [Proc]
|
109
|
+
def default_proc
|
110
|
+
@default
|
111
|
+
end
|
112
|
+
|
113
|
+
# Return the number of key/value pairs in this `Hash`.
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3].size # => 3
|
117
|
+
#
|
118
|
+
# @return [Integer]
|
119
|
+
def size
|
120
|
+
@trie.size
|
121
|
+
end
|
122
|
+
alias length size
|
123
|
+
|
124
|
+
# Return `true` if this `Hash` contains no key/value pairs.
|
125
|
+
#
|
126
|
+
# @return [Boolean]
|
127
|
+
def empty?
|
128
|
+
@trie.empty?
|
129
|
+
end
|
130
|
+
|
131
|
+
# Return `true` if the given key object is present in this `Hash`. More precisely,
|
132
|
+
# return `true` if a key with the same `#hash` code, and which is also `#eql?`
|
133
|
+
# to the given key object is present.
|
134
|
+
#
|
135
|
+
# @example
|
136
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3].key?("B") # => true
|
137
|
+
#
|
138
|
+
# @param key [Object] The key to check for
|
139
|
+
# @return [Boolean]
|
140
|
+
def key?(key)
|
141
|
+
@trie.key?(key)
|
142
|
+
end
|
143
|
+
alias has_key? key?
|
144
|
+
alias include? key?
|
145
|
+
alias member? key?
|
146
|
+
|
147
|
+
# Return `true` if this `Hash` has one or more keys which map to the provided value.
|
148
|
+
#
|
149
|
+
# @example
|
150
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3].value?(2) # => true
|
151
|
+
#
|
152
|
+
# @param value [Object] The value to check for
|
153
|
+
# @return [Boolean]
|
154
|
+
def value?(value)
|
155
|
+
each { |k,v| return true if value == v }
|
156
|
+
false
|
157
|
+
end
|
158
|
+
alias has_value? value?
|
159
|
+
|
160
|
+
# Retrieve the value corresponding to the provided key object. If not found, and
|
161
|
+
# this `Hash` has a default block, the default block is called to provide the
|
162
|
+
# value. Otherwise, return `nil`.
|
163
|
+
#
|
164
|
+
# @example
|
165
|
+
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
166
|
+
# h["B"] # => 2
|
167
|
+
# h.get("B") # => 2
|
168
|
+
# h.get("Elephant") # => nil
|
169
|
+
#
|
170
|
+
# # Immutable Hash with a default proc:
|
171
|
+
# h = Immutable::Hash.new("A" => 1, "B" => 2, "C" => 3) { |key| key.size }
|
172
|
+
# h.get("B") # => 2
|
173
|
+
# h.get("Elephant") # => 8
|
174
|
+
#
|
175
|
+
# @param key [Object] The key to look up
|
176
|
+
# @return [Object]
|
177
|
+
def get(key)
|
178
|
+
entry = @trie.get(key)
|
179
|
+
if entry
|
180
|
+
entry[1]
|
181
|
+
elsif @default
|
182
|
+
@default.call(key)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
alias [] get
|
186
|
+
|
187
|
+
# Retrieve the value corresponding to the given key object, or use the provided
|
188
|
+
# default value or block, or otherwise raise a `KeyError`.
|
189
|
+
#
|
190
|
+
# @overload fetch(key)
|
191
|
+
# Retrieve the value corresponding to the given key, or raise a `KeyError`
|
192
|
+
# if it is not found.
|
193
|
+
# @param key [Object] The key to look up
|
194
|
+
# @overload fetch(key) { |key| ... }
|
195
|
+
# Retrieve the value corresponding to the given key, or call the optional
|
196
|
+
# code block (with the missing key) and get its return value.
|
197
|
+
# @yield [key] The key which was not found
|
198
|
+
# @yieldreturn [Object] Object to return since the key was not found
|
199
|
+
# @param key [Object] The key to look up
|
200
|
+
# @overload fetch(key, default)
|
201
|
+
# Retrieve the value corresponding to the given key, or else return
|
202
|
+
# the provided `default` value.
|
203
|
+
# @param key [Object] The key to look up
|
204
|
+
# @param default [Object] Object to return if the key is not found
|
205
|
+
#
|
206
|
+
# @example
|
207
|
+
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
208
|
+
# h.fetch("B") # => 2
|
209
|
+
# h.fetch("Elephant") # => KeyError: key not found: "Elephant"
|
210
|
+
#
|
211
|
+
# # with a default value:
|
212
|
+
# h.fetch("B", 99) # => 2
|
213
|
+
# h.fetch("Elephant", 99) # => 99
|
214
|
+
#
|
215
|
+
# # with a block:
|
216
|
+
# h.fetch("B") { |key| key.size } # => 2
|
217
|
+
# h.fetch("Elephant") { |key| key.size } # => 8
|
218
|
+
#
|
219
|
+
# @return [Object]
|
220
|
+
def fetch(key, default = Undefined)
|
221
|
+
entry = @trie.get(key)
|
222
|
+
if entry
|
223
|
+
entry[1]
|
224
|
+
elsif block_given?
|
225
|
+
yield(key)
|
226
|
+
elsif default != Undefined
|
227
|
+
default
|
228
|
+
else
|
229
|
+
raise KeyError, "key not found: #{key.inspect}"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Return a new `Hash` with the existing key/value associations, plus an association
|
234
|
+
# between the provided key and value. If an equivalent key is already present, its
|
235
|
+
# associated value will be replaced with the provided one.
|
236
|
+
#
|
237
|
+
# If the `value` argument is missing, but an optional code block is provided,
|
238
|
+
# it will be passed the existing value (or `nil` if there is none) and what it
|
239
|
+
# returns will replace the existing value. This is useful for "transforming"
|
240
|
+
# the value associated with a certain key.
|
241
|
+
#
|
242
|
+
# Avoid mutating objects which are used as keys. `String`s are an exception:
|
243
|
+
# unfrozen `String`s which are used as keys are internally duplicated and
|
244
|
+
# frozen. This matches RubyHash's behaviour.
|
245
|
+
#
|
246
|
+
# @example
|
247
|
+
# h = Immutable::Hash["A" => 1, "B" => 2]
|
248
|
+
# h.put("C", 3)
|
249
|
+
# # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
250
|
+
# h.put("B") { |value| value * 10 }
|
251
|
+
# # => Immutable::Hash["A" => 1, "B" => 20]
|
252
|
+
#
|
253
|
+
# @param key [Object] The key to store
|
254
|
+
# @param value [Object] The value to associate it with
|
255
|
+
# @yield [value] The previously stored value, or `nil` if none.
|
256
|
+
# @yieldreturn [Object] The new value to store
|
257
|
+
# @return [Hash]
|
258
|
+
def put(key, value = yield(get(key)))
|
259
|
+
new_trie = @trie.put(key, value)
|
260
|
+
if new_trie.equal?(@trie)
|
261
|
+
self
|
262
|
+
else
|
263
|
+
self.class.alloc(new_trie, @default)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# @private
|
268
|
+
# @raise NoMethodError
|
269
|
+
def []=(*)
|
270
|
+
raise NoMethodError, "Immutable::Hash doesn't support `[]='; use `put' instead"
|
271
|
+
end
|
272
|
+
|
273
|
+
# Return a new `Hash` with a deeply nested value modified to the result of
|
274
|
+
# the given code block. When traversing the nested `Hash`es and `Vector`s,
|
275
|
+
# non-existing keys are created with empty `Hash` values.
|
276
|
+
#
|
277
|
+
# The code block receives the existing value of the deeply nested key (or
|
278
|
+
# `nil` if it doesn't exist). This is useful for "transforming" the value
|
279
|
+
# associated with a certain key.
|
280
|
+
#
|
281
|
+
# Note that the original `Hash` and sub-`Hash`es and sub-`Vector`s are left
|
282
|
+
# unmodified; new data structure copies are created along the path wherever
|
283
|
+
# needed.
|
284
|
+
#
|
285
|
+
# @example
|
286
|
+
# hash = Immutable::Hash["a" => Immutable::Hash["b" => Immutable::Hash["c" => 42]]]
|
287
|
+
# hash.update_in("a", "b", "c") { |value| value + 5 }
|
288
|
+
# # => Immutable::Hash["a" => Immutable::Hash["b" => Immutable::Hash["c" => 47]]]
|
289
|
+
#
|
290
|
+
# @param key_path [::Array<Object>] List of keys which form the path to the key to be modified
|
291
|
+
# @yield [value] The previously stored value
|
292
|
+
# @yieldreturn [Object] The new value to store
|
293
|
+
# @return [Hash]
|
294
|
+
def update_in(*key_path, &block)
|
295
|
+
if key_path.empty?
|
296
|
+
raise ArgumentError, 'must have at least one key in path'
|
297
|
+
end
|
298
|
+
key = key_path[0]
|
299
|
+
if key_path.size == 1
|
300
|
+
new_value = block.call(get(key))
|
301
|
+
else
|
302
|
+
value = fetch(key, EmptyHash)
|
303
|
+
new_value = value.update_in(*key_path[1..-1], &block)
|
304
|
+
end
|
305
|
+
put(key, new_value)
|
306
|
+
end
|
307
|
+
|
308
|
+
# An alias for {#put} to match RubyHash's API. Does not support {#put}'s
|
309
|
+
# block form.
|
310
|
+
#
|
311
|
+
# @see #put
|
312
|
+
# @param key [Object] The key to store
|
313
|
+
# @param value [Object] The value to associate it with
|
314
|
+
# @return [Hash]
|
315
|
+
def store(key, value)
|
316
|
+
put(key, value)
|
317
|
+
end
|
318
|
+
|
319
|
+
# Return a new `Hash` with `key` removed. If `key` is not present, return
|
320
|
+
# `self`.
|
321
|
+
#
|
322
|
+
# @example
|
323
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3].delete("B")
|
324
|
+
# # => Immutable::Hash["A" => 1, "C" => 3]
|
325
|
+
#
|
326
|
+
# @param key [Object] The key to remove
|
327
|
+
# @return [Hash]
|
328
|
+
def delete(key)
|
329
|
+
derive_new_hash(@trie.delete(key))
|
330
|
+
end
|
331
|
+
|
332
|
+
# Call the block once for each key/value pair in this `Hash`, passing the key/value
|
333
|
+
# pair as parameters. No specific iteration order is guaranteed, though the order will
|
334
|
+
# be stable for any particular `Hash`.
|
335
|
+
#
|
336
|
+
# @example
|
337
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3].each { |k, v| puts "k=#{k} v=#{v}" }
|
338
|
+
#
|
339
|
+
# k=A v=1
|
340
|
+
# k=C v=3
|
341
|
+
# k=B v=2
|
342
|
+
# # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
343
|
+
#
|
344
|
+
# @yield [key, value] Once for each key/value pair.
|
345
|
+
# @return [self]
|
346
|
+
def each(&block)
|
347
|
+
return to_enum if not block_given?
|
348
|
+
@trie.each(&block)
|
349
|
+
self
|
350
|
+
end
|
351
|
+
alias each_pair each
|
352
|
+
|
353
|
+
# Call the block once for each key/value pair in this `Hash`, passing the key/value
|
354
|
+
# pair as parameters. Iteration order will be the opposite of {#each}.
|
355
|
+
#
|
356
|
+
# @example
|
357
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3].reverse_each { |k, v| puts "k=#{k} v=#{v}" }
|
358
|
+
#
|
359
|
+
# k=B v=2
|
360
|
+
# k=C v=3
|
361
|
+
# k=A v=1
|
362
|
+
# # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
363
|
+
#
|
364
|
+
# @yield [key, value] Once for each key/value pair.
|
365
|
+
# @return [self]
|
366
|
+
def reverse_each(&block)
|
367
|
+
return enum_for(:reverse_each) if not block_given?
|
368
|
+
@trie.reverse_each(&block)
|
369
|
+
self
|
370
|
+
end
|
371
|
+
|
372
|
+
# Call the block once for each key/value pair in this `Hash`, passing the key as a
|
373
|
+
# parameter. Ordering guarantees are the same as {#each}.
|
374
|
+
#
|
375
|
+
# @example
|
376
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3].each_key { |k| puts "k=#{k}" }
|
377
|
+
#
|
378
|
+
# k=A
|
379
|
+
# k=C
|
380
|
+
# k=B
|
381
|
+
# # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
382
|
+
#
|
383
|
+
# @yield [key] Once for each key/value pair.
|
384
|
+
# @return [self]
|
385
|
+
def each_key
|
386
|
+
return enum_for(:each_key) if not block_given?
|
387
|
+
@trie.each { |k,v| yield k }
|
388
|
+
self
|
389
|
+
end
|
390
|
+
|
391
|
+
# Call the block once for each key/value pair in this `Hash`, passing the value as a
|
392
|
+
# parameter. Ordering guarantees are the same as {#each}.
|
393
|
+
#
|
394
|
+
# @example
|
395
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3].each_value { |v| puts "v=#{v}" }
|
396
|
+
#
|
397
|
+
# v=1
|
398
|
+
# v=3
|
399
|
+
# v=2
|
400
|
+
# # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
401
|
+
#
|
402
|
+
# @yield [value] Once for each key/value pair.
|
403
|
+
# @return [self]
|
404
|
+
def each_value
|
405
|
+
return enum_for(:each_value) if not block_given?
|
406
|
+
@trie.each { |k,v| yield v }
|
407
|
+
self
|
408
|
+
end
|
409
|
+
|
410
|
+
# Call the block once for each key/value pair in this `Hash`, passing the key/value
|
411
|
+
# pair as parameters. The block should return a `[key, value]` array each time.
|
412
|
+
# All the returned `[key, value]` arrays will be gathered into a new `Hash`.
|
413
|
+
#
|
414
|
+
# @example
|
415
|
+
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
416
|
+
# h.map { |k, v| ["new-#{k}", v * v] }
|
417
|
+
# # => Hash["new-C" => 9, "new-B" => 4, "new-A" => 1]
|
418
|
+
#
|
419
|
+
# @yield [key, value] Once for each key/value pair.
|
420
|
+
# @return [Hash]
|
421
|
+
def map
|
422
|
+
return enum_for(:map) unless block_given?
|
423
|
+
return self if empty?
|
424
|
+
self.class.new(super, &@default)
|
425
|
+
end
|
426
|
+
alias collect map
|
427
|
+
|
428
|
+
# Return a new `Hash` with all the key/value pairs for which the block returns true.
|
429
|
+
#
|
430
|
+
# @example
|
431
|
+
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
432
|
+
# h.select { |k, v| v >= 2 }
|
433
|
+
# # => Immutable::Hash["B" => 2, "C" => 3]
|
434
|
+
#
|
435
|
+
# @yield [key, value] Once for each key/value pair.
|
436
|
+
# @yieldreturn Truthy if this pair should be present in the new `Hash`.
|
437
|
+
# @return [Hash]
|
438
|
+
def select(&block)
|
439
|
+
return enum_for(:select) unless block_given?
|
440
|
+
derive_new_hash(@trie.select(&block))
|
441
|
+
end
|
442
|
+
alias find_all select
|
443
|
+
alias keep_if select
|
444
|
+
|
445
|
+
# Yield `[key, value]` pairs until one is found for which the block returns true.
|
446
|
+
# Return that `[key, value]` pair. If the block never returns true, return `nil`.
|
447
|
+
#
|
448
|
+
# @example
|
449
|
+
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
450
|
+
# h.find { |k, v| v.even? }
|
451
|
+
# # => ["B", 2]
|
452
|
+
#
|
453
|
+
# @return [Array]
|
454
|
+
# @yield [key, value] At most once for each key/value pair, until the block returns `true`.
|
455
|
+
# @yieldreturn Truthy to halt iteration and return the yielded key/value pair.
|
456
|
+
def find
|
457
|
+
return enum_for(:find) unless block_given?
|
458
|
+
each { |entry| return entry if yield entry }
|
459
|
+
nil
|
460
|
+
end
|
461
|
+
alias detect find
|
462
|
+
|
463
|
+
# Return a new `Hash` containing all the key/value pairs from this `Hash` and
|
464
|
+
# `other`. If no block is provided, the value for entries with colliding keys
|
465
|
+
# will be that from `other`. Otherwise, the value for each duplicate key is
|
466
|
+
# determined by calling the block.
|
467
|
+
#
|
468
|
+
# `other` can be an `Immutable::Hash`, a built-in Ruby `Hash`, or any `Enumerable`
|
469
|
+
# object which yields `[key, value]` pairs.
|
470
|
+
#
|
471
|
+
# @example
|
472
|
+
# h1 = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
473
|
+
# h2 = Immutable::Hash["C" => 70, "D" => 80]
|
474
|
+
# h1.merge(h2)
|
475
|
+
# # => Immutable::Hash["C" => 70, "A" => 1, "D" => 80, "B" => 2]
|
476
|
+
# h1.merge(h2) { |key, v1, v2| v1 + v2 }
|
477
|
+
# # => Immutable::Hash["C" => 73, "A" => 1, "D" => 80, "B" => 2]
|
478
|
+
#
|
479
|
+
# @param other [::Enumerable] The collection to merge with
|
480
|
+
# @yieldparam key [Object] The key which was present in both collections
|
481
|
+
# @yieldparam my_value [Object] The associated value from this `Hash`
|
482
|
+
# @yieldparam other_value [Object] The associated value from the other collection
|
483
|
+
# @yieldreturn [Object] The value to associate this key with in the new `Hash`
|
484
|
+
# @return [Hash]
|
485
|
+
def merge(other)
|
486
|
+
trie = if block_given?
|
487
|
+
other.reduce(@trie) do |trie, (key, value)|
|
488
|
+
if (entry = trie.get(key))
|
489
|
+
trie.put(key, yield(key, entry[1], value))
|
490
|
+
else
|
491
|
+
trie.put(key, value)
|
492
|
+
end
|
493
|
+
end
|
494
|
+
else
|
495
|
+
@trie.bulk_put(other)
|
496
|
+
end
|
497
|
+
|
498
|
+
derive_new_hash(trie)
|
499
|
+
end
|
500
|
+
|
501
|
+
# Retrieve the value corresponding to the given key object, or use the provided
|
502
|
+
# default value or block, or otherwise raise a `KeyError`.
|
503
|
+
#
|
504
|
+
# @overload fetch(key)
|
505
|
+
# Retrieve the value corresponding to the given key, or raise a `KeyError`
|
506
|
+
# if it is not found.
|
507
|
+
# @param key [Object] The key to look up
|
508
|
+
# @overload fetch(key) { |key| ... }
|
509
|
+
|
510
|
+
# Return a sorted {Vector} which contains all the `[key, value]` pairs in
|
511
|
+
# this `Hash` as two-element `Array`s.
|
512
|
+
#
|
513
|
+
# @overload sort
|
514
|
+
# Uses `#<=>` to determine sorted order.
|
515
|
+
# @overload sort { |(k1, v1), (k2, v2)| ... }
|
516
|
+
# Uses the block as a comparator to determine sorted order.
|
517
|
+
#
|
518
|
+
# @example
|
519
|
+
# h = Immutable::Hash["Dog" => 1, "Elephant" => 2, "Lion" => 3]
|
520
|
+
# h.sort { |(k1, v1), (k2, v2)| k1.size <=> k2.size }
|
521
|
+
# # => Immutable::Vector[["Dog", 1], ["Lion", 3], ["Elephant", 2]]
|
522
|
+
# @yield [(k1, v1), (k2, v2)] Any number of times with different pairs of key/value associations.
|
523
|
+
# @yieldreturn [Integer] Negative if the first pair should be sorted
|
524
|
+
# lower, positive if the latter pair, or 0 if equal.
|
525
|
+
#
|
526
|
+
# @see ::Enumerable#sort
|
527
|
+
#
|
528
|
+
# @return [Vector]
|
529
|
+
def sort
|
530
|
+
Vector.new(super)
|
531
|
+
end
|
532
|
+
|
533
|
+
# Return a {Vector} which contains all the `[key, value]` pairs in this `Hash`
|
534
|
+
# as two-element Arrays. The order which the pairs will appear in is determined by
|
535
|
+
# passing each pair to the code block to obtain a sort key object, and comparing
|
536
|
+
# the sort keys using `#<=>`.
|
537
|
+
#
|
538
|
+
# @see ::Enumerable#sort_by
|
539
|
+
#
|
540
|
+
# @example
|
541
|
+
# h = Immutable::Hash["Dog" => 1, "Elephant" => 2, "Lion" => 3]
|
542
|
+
# h.sort_by { |key, value| key.size }
|
543
|
+
# # => Immutable::Vector[["Dog", 1], ["Lion", 3], ["Elephant", 2]]
|
544
|
+
#
|
545
|
+
# @yield [key, value] Once for each key/value pair.
|
546
|
+
# @yieldreturn a sort key object for the yielded pair.
|
547
|
+
# @return [Vector]
|
548
|
+
def sort_by
|
549
|
+
Vector.new(super)
|
550
|
+
end
|
551
|
+
|
552
|
+
# Return a new `Hash` with the associations for all of the given `keys` removed.
|
553
|
+
#
|
554
|
+
# @example
|
555
|
+
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
556
|
+
# h.except("A", "C") # => Immutable::Hash["B" => 2]
|
557
|
+
#
|
558
|
+
# @param keys [Array] The keys to remove
|
559
|
+
# @return [Hash]
|
560
|
+
def except(*keys)
|
561
|
+
keys.reduce(self) { |hash, key| hash.delete(key) }
|
562
|
+
end
|
563
|
+
|
564
|
+
# Return a new `Hash` with only the associations for the `wanted` keys retained.
|
565
|
+
#
|
566
|
+
# @example
|
567
|
+
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
568
|
+
# h.slice("B", "C") # => Immutable::Hash["B" => 2, "C" => 3]
|
569
|
+
#
|
570
|
+
# @param wanted [::Enumerable] The keys to retain
|
571
|
+
# @return [Hash]
|
572
|
+
def slice(*wanted)
|
573
|
+
trie = Trie.new(0)
|
574
|
+
wanted.each { |key| trie.put!(key, get(key)) if key?(key) }
|
575
|
+
self.class.alloc(trie, @default)
|
576
|
+
end
|
577
|
+
|
578
|
+
# Return a {Vector} of the values which correspond to the `wanted` keys.
|
579
|
+
# If any of the `wanted` keys are not present in this `Hash`, `nil` will be
|
580
|
+
# placed instead, or the result of the default proc (if one is defined),
|
581
|
+
# similar to the behavior of {#get}.
|
582
|
+
#
|
583
|
+
# @example
|
584
|
+
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
585
|
+
# h.values_at("B", "A", "D") # => Immutable::Vector[2, 1, nil]
|
586
|
+
#
|
587
|
+
# @param wanted [Array] The keys to retrieve
|
588
|
+
# @return [Vector]
|
589
|
+
def values_at(*wanted)
|
590
|
+
Vector.new(wanted.map { |key| get(key) }.freeze)
|
591
|
+
end
|
592
|
+
|
593
|
+
# Return a {Vector} of the values which correspond to the `wanted` keys.
|
594
|
+
# If any of the `wanted` keys are not present in this `Hash`, raise `KeyError`
|
595
|
+
# exception.
|
596
|
+
#
|
597
|
+
# @example
|
598
|
+
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
599
|
+
# h.fetch_values("C", "A") # => Immutable::Vector[3, 1]
|
600
|
+
# h.fetch_values("C", "Z") # => KeyError: key not found: "Z"
|
601
|
+
#
|
602
|
+
# @param wanted [Array] The keys to retrieve
|
603
|
+
# @return [Vector]
|
604
|
+
def fetch_values(*wanted)
|
605
|
+
array = wanted.map { |key| fetch(key) }
|
606
|
+
Vector.new(array.freeze)
|
607
|
+
end
|
608
|
+
|
609
|
+
# Return the value of successively indexing into a nested collection.
|
610
|
+
# If any of the keys is not present, return `nil`.
|
611
|
+
#
|
612
|
+
# @example
|
613
|
+
# h = Immutable::Hash[a: 9, b: Immutable::Hash[c: 'a', d: 4], e: nil]
|
614
|
+
# h.dig(:b, :c) # => "a"
|
615
|
+
# h.dig(:b, :f) # => nil
|
616
|
+
#
|
617
|
+
# @return [Object]
|
618
|
+
def dig(key, *rest)
|
619
|
+
value = self[key]
|
620
|
+
if rest.empty? || value.nil?
|
621
|
+
value
|
622
|
+
else
|
623
|
+
value.dig(*rest)
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
# Return a new {Set} containing the keys from this `Hash`.
|
628
|
+
#
|
629
|
+
# @example
|
630
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3, "D" => 2].keys
|
631
|
+
# # => Immutable::Set["D", "C", "B", "A"]
|
632
|
+
#
|
633
|
+
# @return [Set]
|
634
|
+
def keys
|
635
|
+
Set.alloc(@trie)
|
636
|
+
end
|
637
|
+
|
638
|
+
# Return a new {Vector} populated with the values from this `Hash`.
|
639
|
+
#
|
640
|
+
# @example
|
641
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3, "D" => 2].values
|
642
|
+
# # => Immutable::Vector[2, 3, 2, 1]
|
643
|
+
#
|
644
|
+
# @return [Vector]
|
645
|
+
def values
|
646
|
+
Vector.new(each_value.to_a.freeze)
|
647
|
+
end
|
648
|
+
|
649
|
+
# Return a new `Hash` created by using keys as values and values as keys.
|
650
|
+
# If there are multiple values which are equivalent (as determined by `#hash` and
|
651
|
+
# `#eql?`), only one out of each group of equivalent values will be
|
652
|
+
# retained. Which one specifically is undefined.
|
653
|
+
#
|
654
|
+
# @example
|
655
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3, "D" => 2].invert
|
656
|
+
# # => Immutable::Hash[1 => "A", 3 => "C", 2 => "B"]
|
657
|
+
#
|
658
|
+
# @return [Hash]
|
659
|
+
def invert
|
660
|
+
pairs = []
|
661
|
+
each { |k,v| pairs << [v, k] }
|
662
|
+
self.class.new(pairs, &@default)
|
663
|
+
end
|
664
|
+
|
665
|
+
# Return a new {Vector} which is a one-dimensional flattening of this `Hash`.
|
666
|
+
# If `level` is 1, all the `[key, value]` pairs in the hash will be concatenated
|
667
|
+
# into one {Vector}. If `level` is greater than 1, keys or values which are
|
668
|
+
# themselves `Array`s or {Vector}s will be recursively flattened into the output
|
669
|
+
# {Vector}. The depth to which that flattening will be recursively applied is
|
670
|
+
# determined by `level`.
|
671
|
+
#
|
672
|
+
# As a special case, if `level` is 0, each `[key, value]` pair will be a
|
673
|
+
# separate element in the returned {Vector}.
|
674
|
+
#
|
675
|
+
# @example
|
676
|
+
# h = Immutable::Hash["A" => 1, "B" => [2, 3, 4]]
|
677
|
+
# h.flatten
|
678
|
+
# # => Immutable::Vector["A", 1, "B", [2, 3, 4]]
|
679
|
+
# h.flatten(2)
|
680
|
+
# # => Immutable::Vector["A", 1, "B", 2, 3, 4]
|
681
|
+
#
|
682
|
+
# @param level [Integer] The number of times to recursively flatten the `[key, value]` pairs in this `Hash`.
|
683
|
+
# @return [Vector]
|
684
|
+
def flatten(level = 1)
|
685
|
+
return Vector.new(self) if level == 0
|
686
|
+
array = []
|
687
|
+
each { |k,v| array << k; array << v }
|
688
|
+
array.flatten!(level-1) if level > 1
|
689
|
+
Vector.new(array.freeze)
|
690
|
+
end
|
691
|
+
|
692
|
+
# Searches through the `Hash`, comparing `obj` with each key (using `#==`).
|
693
|
+
# When a matching key is found, return the `[key, value]` pair as an array.
|
694
|
+
# Return `nil` if no match is found.
|
695
|
+
#
|
696
|
+
# @example
|
697
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3].assoc("B") # => ["B", 2]
|
698
|
+
#
|
699
|
+
# @param obj [Object] The key to search for (using #==)
|
700
|
+
# @return [Array]
|
701
|
+
def assoc(obj)
|
702
|
+
each { |entry| return entry if obj == entry[0] }
|
703
|
+
nil
|
704
|
+
end
|
705
|
+
|
706
|
+
# Searches through the `Hash`, comparing `obj` with each value (using `#==`).
|
707
|
+
# When a matching value is found, return the `[key, value]` pair as an array.
|
708
|
+
# Return `nil` if no match is found.
|
709
|
+
#
|
710
|
+
# @example
|
711
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3].rassoc(2) # => ["B", 2]
|
712
|
+
#
|
713
|
+
# @param obj [Object] The value to search for (using #==)
|
714
|
+
# @return [Array]
|
715
|
+
def rassoc(obj)
|
716
|
+
each { |entry| return entry if obj == entry[1] }
|
717
|
+
nil
|
718
|
+
end
|
719
|
+
|
720
|
+
# Searches through the `Hash`, comparing `value` with each value (using `#==`).
|
721
|
+
# When a matching value is found, return its associated key object.
|
722
|
+
# Return `nil` if no match is found.
|
723
|
+
#
|
724
|
+
# @example
|
725
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3].key(2) # => "B"
|
726
|
+
#
|
727
|
+
# @param value [Object] The value to search for (using #==)
|
728
|
+
# @return [Object]
|
729
|
+
def key(value)
|
730
|
+
each { |entry| return entry[0] if value == entry[1] }
|
731
|
+
nil
|
732
|
+
end
|
733
|
+
|
734
|
+
# Return a randomly chosen `[key, value]` pair from this `Hash`. If the hash is empty,
|
735
|
+
# return `nil`.
|
736
|
+
#
|
737
|
+
# @example
|
738
|
+
# Immutable::Hash["A" => 1, "B" => 2, "C" => 3].sample
|
739
|
+
# # => ["C", 3]
|
740
|
+
#
|
741
|
+
# @return [Array]
|
742
|
+
def sample
|
743
|
+
@trie.at(rand(size))
|
744
|
+
end
|
745
|
+
|
746
|
+
# Return an empty `Hash` instance, of the same class as this one. Useful if you
|
747
|
+
# have multiple subclasses of `Hash` and want to treat them polymorphically.
|
748
|
+
# Maintains the default block, if there is one.
|
749
|
+
#
|
750
|
+
# @return [Hash]
|
751
|
+
def clear
|
752
|
+
if @default
|
753
|
+
self.class.alloc(EmptyTrie, @default)
|
754
|
+
else
|
755
|
+
self.class.empty
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
# Return true if `other` has the same type and contents as this `Hash`.
|
760
|
+
#
|
761
|
+
# @param other [Object] The collection to compare with
|
762
|
+
# @return [Boolean]
|
763
|
+
def eql?(other)
|
764
|
+
return true if other.equal?(self)
|
765
|
+
instance_of?(other.class) && @trie.eql?(other.instance_variable_get(:@trie))
|
766
|
+
end
|
767
|
+
|
768
|
+
# Return true if `other` has the same contents as this `Hash`. Will convert
|
769
|
+
# `other` to a Ruby `Hash` using `#to_hash` if necessary.
|
770
|
+
#
|
771
|
+
# @param other [Object] The object to compare with
|
772
|
+
# @return [Boolean]
|
773
|
+
def ==(other)
|
774
|
+
eql?(other) || (other.respond_to?(:to_hash) && to_hash == other.to_hash)
|
775
|
+
end
|
776
|
+
|
777
|
+
# Return true if this `Hash` is a proper superset of `other`, which means
|
778
|
+
# all `other`'s keys are contained in this `Hash` with identical
|
779
|
+
# values, and the two hashes are not identical.
|
780
|
+
#
|
781
|
+
# @param other [Immutable::Hash] The object to compare with
|
782
|
+
# @return [Boolean]
|
783
|
+
def >(other)
|
784
|
+
self != other && self >= other
|
785
|
+
end
|
786
|
+
|
787
|
+
# Return true if this `Hash` is a superset of `other`, which means all
|
788
|
+
# `other`'s keys are contained in this `Hash` with identical values.
|
789
|
+
#
|
790
|
+
# @param other [Immutable::Hash] The object to compare with
|
791
|
+
# @return [Boolean]
|
792
|
+
def >=(other)
|
793
|
+
other.each do |key, value|
|
794
|
+
if self[key] != value
|
795
|
+
return false
|
796
|
+
end
|
797
|
+
end
|
798
|
+
true
|
799
|
+
end
|
800
|
+
|
801
|
+
# Return true if this `Hash` is a proper subset of `other`, which means all
|
802
|
+
# its keys are contained in `other` with the identical values, and the two
|
803
|
+
# hashes are not identical.
|
804
|
+
#
|
805
|
+
# @param other [Immutable::Hash] The object to compare with
|
806
|
+
# @return [Boolean]
|
807
|
+
def <(other)
|
808
|
+
other > self
|
809
|
+
end
|
810
|
+
|
811
|
+
# Return true if this `Hash` is a subset of `other`, which means all its
|
812
|
+
# keys are contained in `other` with the identical values, and the two
|
813
|
+
# hashes are not identical.
|
814
|
+
#
|
815
|
+
# @param other [Immutable::Hash] The object to compare with
|
816
|
+
# @return [Boolean]
|
817
|
+
def <=(other)
|
818
|
+
other >= self
|
819
|
+
end
|
820
|
+
|
821
|
+
# See `Object#hash`.
|
822
|
+
# @return [Integer]
|
823
|
+
def hash
|
824
|
+
keys.to_a.sort.reduce(0) do |hash, key|
|
825
|
+
(hash << 32) - hash + key.hash + get(key).hash
|
826
|
+
end
|
827
|
+
end
|
828
|
+
|
829
|
+
# Return the contents of this `Hash` as a programmer-readable `String`. If all the
|
830
|
+
# keys and values are serializable as Ruby literal strings, the returned string can
|
831
|
+
# be passed to `eval` to reconstitute an equivalent `Hash`. The default
|
832
|
+
# block (if there is one) will be lost when doing this, however.
|
833
|
+
#
|
834
|
+
# @return [String]
|
835
|
+
def inspect
|
836
|
+
result = "#{self.class}["
|
837
|
+
i = 0
|
838
|
+
each do |key, val|
|
839
|
+
result << ', ' if i > 0
|
840
|
+
result << key.inspect << ' => ' << val.inspect
|
841
|
+
i += 1
|
842
|
+
end
|
843
|
+
result << ']'
|
844
|
+
end
|
845
|
+
|
846
|
+
# Return `self`. Since this is an immutable object duplicates are
|
847
|
+
# equivalent.
|
848
|
+
# @return [Hash]
|
849
|
+
def dup
|
850
|
+
self
|
851
|
+
end
|
852
|
+
alias clone dup
|
853
|
+
|
854
|
+
# Allows this `Hash` to be printed at the `pry` console, or using `pp` (from the
|
855
|
+
# Ruby standard library), in a way which takes the amount of horizontal space on
|
856
|
+
# the screen into account, and which indents nested structures to make them easier
|
857
|
+
# to read.
|
858
|
+
#
|
859
|
+
# @private
|
860
|
+
def pretty_print(pp)
|
861
|
+
pp.group(1, "#{self.class}[", ']') do
|
862
|
+
pp.breakable ''
|
863
|
+
pp.seplist(self, nil) do |key, val|
|
864
|
+
pp.group do
|
865
|
+
key.pretty_print(pp)
|
866
|
+
pp.text ' => '
|
867
|
+
pp.group(1) do
|
868
|
+
pp.breakable ''
|
869
|
+
val.pretty_print(pp)
|
870
|
+
end
|
871
|
+
end
|
872
|
+
end
|
873
|
+
end
|
874
|
+
end
|
875
|
+
|
876
|
+
# Convert this `Immutable::Hash` to an instance of Ruby's built-in `Hash`.
|
877
|
+
#
|
878
|
+
# @return [::Hash]
|
879
|
+
def to_hash
|
880
|
+
output = {}
|
881
|
+
each do |key, value|
|
882
|
+
output[key] = value
|
883
|
+
end
|
884
|
+
output
|
885
|
+
end
|
886
|
+
alias to_h to_hash
|
887
|
+
|
888
|
+
# Return a `Proc` which accepts a key as an argument and returns the value.
|
889
|
+
# The `Proc` behaves like {#get} (when the key is missing, it returns nil or
|
890
|
+
# the result of the default proc).
|
891
|
+
#
|
892
|
+
# @example
|
893
|
+
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
894
|
+
# h.to_proc.call("B")
|
895
|
+
# # => 2
|
896
|
+
# ["A", "C", "X"].map(&h) # The & is short for .to_proc in Ruby
|
897
|
+
# # => [1, 3, nil]
|
898
|
+
#
|
899
|
+
# @return [Proc]
|
900
|
+
def to_proc
|
901
|
+
lambda { |key| get(key) }
|
902
|
+
end
|
903
|
+
|
904
|
+
# @return [::Hash]
|
905
|
+
# @private
|
906
|
+
def marshal_dump
|
907
|
+
to_hash
|
908
|
+
end
|
909
|
+
|
910
|
+
# @private
|
911
|
+
def marshal_load(dictionary)
|
912
|
+
@trie = Trie[dictionary]
|
913
|
+
end
|
914
|
+
|
915
|
+
private
|
916
|
+
|
917
|
+
# Return a new `Hash` which is derived from this one, using a modified {Trie}.
|
918
|
+
# The new `Hash` will retain the existing default block, if there is one.
|
919
|
+
#
|
920
|
+
def derive_new_hash(trie)
|
921
|
+
if trie.equal?(@trie)
|
922
|
+
self
|
923
|
+
elsif trie.empty?
|
924
|
+
if @default
|
925
|
+
self.class.alloc(EmptyTrie, @default)
|
926
|
+
else
|
927
|
+
self.class.empty
|
928
|
+
end
|
929
|
+
else
|
930
|
+
self.class.alloc(trie, @default)
|
931
|
+
end
|
932
|
+
end
|
933
|
+
end
|
934
|
+
|
935
|
+
# The canonical empty `Hash`. Returned by `Hash[]` when
|
936
|
+
# invoked with no arguments; also returned by `Hash.empty`. Prefer using this
|
937
|
+
# one rather than creating many empty hashes using `Hash.new`.
|
938
|
+
#
|
939
|
+
# @private
|
940
|
+
EmptyHash = Immutable::Hash.empty
|
941
|
+
|
942
|
+
|
943
|
+
# A `Vector` is an ordered, integer-indexed collection of objects. Like
|
944
|
+
# Ruby's `Array`, `Vector` indexing starts at zero and negative indexes count
|
945
|
+
# back from the end.
|
946
|
+
#
|
947
|
+
# `Vector` has a similar interface to `Array`. The main difference is methods
|
948
|
+
# that would destructively update an `Array` (such as {#insert} or
|
949
|
+
# {#delete_at}) instead return new `Vectors` and leave the existing one
|
950
|
+
# unchanged.
|
951
|
+
#
|
952
|
+
# ### Creating New Vectors
|
953
|
+
#
|
954
|
+
# Immutable::Vector.new([:first, :second, :third])
|
955
|
+
# Immutable::Vector[1, 2, 3, 4, 5]
|
956
|
+
#
|
957
|
+
# ### Retrieving Items from Vectors
|
958
|
+
#
|
959
|
+
# vector = Immutable::Vector[1, 2, 3, 4, 5]
|
960
|
+
#
|
961
|
+
# vector[0] # => 1
|
962
|
+
# vector[-1] # => 5
|
963
|
+
# vector[0,3] # => Immutable::Vector[1, 2, 3]
|
964
|
+
# vector[1..-1] # => Immutable::Vector[2, 3, 4, 5]
|
965
|
+
# vector.first # => 1
|
966
|
+
# vector.last # => 5
|
967
|
+
#
|
968
|
+
# ### Creating Modified Vectors
|
969
|
+
#
|
970
|
+
# vector.add(6) # => Immutable::Vector[1, 2, 3, 4, 5, 6]
|
971
|
+
# vector.insert(1, :a, :b) # => Immutable::Vector[1, :a, :b, 2, 3, 4, 5]
|
972
|
+
# vector.delete_at(2) # => Immutable::Vector[1, 2, 4, 5]
|
973
|
+
# vector + [6, 7] # => Immutable::Vector[1, 2, 3, 4, 5, 6, 7]
|
974
|
+
#
|
975
|
+
class Vector
|
976
|
+
include Immutable::Enumerable
|
977
|
+
|
978
|
+
# @private
|
979
|
+
BLOCK_SIZE = 32
|
980
|
+
# @private
|
981
|
+
INDEX_MASK = BLOCK_SIZE - 1
|
982
|
+
# @private
|
983
|
+
BITS_PER_LEVEL = 5
|
984
|
+
|
985
|
+
# Return the number of items in this `Vector`
|
986
|
+
# @return [Integer]
|
987
|
+
attr_reader :size
|
988
|
+
alias length size
|
989
|
+
|
990
|
+
class << self
|
991
|
+
# Create a new `Vector` populated with the given items.
|
992
|
+
# @return [Vector]
|
993
|
+
def [](*items)
|
994
|
+
new(items.freeze)
|
995
|
+
end
|
996
|
+
|
997
|
+
# Return an empty `Vector`. If used on a subclass, returns an empty instance
|
998
|
+
# of that class.
|
999
|
+
#
|
1000
|
+
# @return [Vector]
|
1001
|
+
def empty
|
1002
|
+
@empty ||= new
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
# "Raw" allocation of a new `Vector`. Used internally to create a new
|
1006
|
+
# instance quickly after building a modified trie.
|
1007
|
+
#
|
1008
|
+
# @return [Vector]
|
1009
|
+
# @private
|
1010
|
+
def alloc(root, size, levels)
|
1011
|
+
obj = allocate
|
1012
|
+
obj.instance_variable_set(:@root, root)
|
1013
|
+
obj.instance_variable_set(:@size, size)
|
1014
|
+
obj.instance_variable_set(:@levels, levels)
|
1015
|
+
obj.freeze
|
1016
|
+
end
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
def initialize(items=[].freeze)
|
1020
|
+
items = items.to_a
|
1021
|
+
if items.size <= 32
|
1022
|
+
items = items.dup.freeze if !items.frozen?
|
1023
|
+
@root, @size, @levels = items, items.size, 0
|
1024
|
+
else
|
1025
|
+
root, size, levels = items, items.size, 0
|
1026
|
+
while root.size > 32
|
1027
|
+
root = root.each_slice(32).to_a
|
1028
|
+
levels += 1
|
1029
|
+
end
|
1030
|
+
@root, @size, @levels = root.freeze, size, levels
|
1031
|
+
end
|
1032
|
+
freeze
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
# Return `true` if this `Vector` contains no items.
|
1036
|
+
#
|
1037
|
+
# @return [Boolean]
|
1038
|
+
def empty?
|
1039
|
+
@size == 0
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
# Return the first item in the `Vector`. If the vector is empty, return `nil`.
|
1043
|
+
#
|
1044
|
+
# @example
|
1045
|
+
# Immutable::Vector["A", "B", "C"].first # => "A"
|
1046
|
+
#
|
1047
|
+
# @return [Object]
|
1048
|
+
def first
|
1049
|
+
get(0)
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
# Return the last item in the `Vector`. If the vector is empty, return `nil`.
|
1053
|
+
#
|
1054
|
+
# @example
|
1055
|
+
# Immutable::Vector["A", "B", "C"].last # => "C"
|
1056
|
+
#
|
1057
|
+
# @return [Object]
|
1058
|
+
def last
|
1059
|
+
get(-1)
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
# Return a new `Vector` with `item` added after the last occupied position.
|
1063
|
+
#
|
1064
|
+
# @example
|
1065
|
+
# Immutable::Vector[1, 2].add(99) # => Immutable::Vector[1, 2, 99]
|
1066
|
+
#
|
1067
|
+
# @param item [Object] The object to insert at the end of the vector
|
1068
|
+
# @return [Vector]
|
1069
|
+
def add(item)
|
1070
|
+
update_root(@size, item)
|
1071
|
+
end
|
1072
|
+
alias << add
|
1073
|
+
alias push add
|
1074
|
+
|
1075
|
+
# Return a new `Vector` with a new value at the given `index`. If `index`
|
1076
|
+
# is greater than the length of the vector, the returned vector will be
|
1077
|
+
# padded with `nil`s to the correct size.
|
1078
|
+
#
|
1079
|
+
# @overload set(index, item)
|
1080
|
+
# Return a new `Vector` with the item at `index` replaced by `item`.
|
1081
|
+
#
|
1082
|
+
# @param item [Object] The object to insert into that position
|
1083
|
+
# @example
|
1084
|
+
# Immutable::Vector[1, 2, 3, 4].set(2, 99)
|
1085
|
+
# # => Immutable::Vector[1, 2, 99, 4]
|
1086
|
+
# Immutable::Vector[1, 2, 3, 4].set(-1, 99)
|
1087
|
+
# # => Immutable::Vector[1, 2, 3, 99]
|
1088
|
+
# Immutable::Vector[].set(2, 99)
|
1089
|
+
# # => Immutable::Vector[nil, nil, 99]
|
1090
|
+
#
|
1091
|
+
# @overload set(index)
|
1092
|
+
# Return a new `Vector` with the item at `index` replaced by the return
|
1093
|
+
# value of the block.
|
1094
|
+
#
|
1095
|
+
# @yield (existing) Once with the existing value at the given `index`.
|
1096
|
+
# @example
|
1097
|
+
# Immutable::Vector[1, 2, 3, 4].set(2) { |v| v * 10 }
|
1098
|
+
# # => Immutable::Vector[1, 2, 30, 4]
|
1099
|
+
#
|
1100
|
+
# @param index [Integer] The index to update. May be negative.
|
1101
|
+
# @return [Vector]
|
1102
|
+
def set(index, item = yield(get(index)))
|
1103
|
+
raise IndexError, "index #{index} outside of vector bounds" if index < -@size
|
1104
|
+
index += @size if index < 0
|
1105
|
+
if index > @size
|
1106
|
+
suffix = Array.new(index - @size, nil)
|
1107
|
+
suffix << item
|
1108
|
+
replace_suffix(@size, suffix)
|
1109
|
+
else
|
1110
|
+
update_root(index, item)
|
1111
|
+
end
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
# Return a new `Vector` with a deeply nested value modified to the result
|
1115
|
+
# of the given code block. When traversing the nested `Vector`s and
|
1116
|
+
# `Hash`es, non-existing keys are created with empty `Hash` values.
|
1117
|
+
#
|
1118
|
+
# The code block receives the existing value of the deeply nested key (or
|
1119
|
+
# `nil` if it doesn't exist). This is useful for "transforming" the value
|
1120
|
+
# associated with a certain key.
|
1121
|
+
#
|
1122
|
+
# Note that the original `Vector` and sub-`Vector`s and sub-`Hash`es are
|
1123
|
+
# left unmodified; new data structure copies are created along the path
|
1124
|
+
# wherever needed.
|
1125
|
+
#
|
1126
|
+
# @example
|
1127
|
+
# v = Immutable::Vector[123, 456, 789, Immutable::Hash["a" => Immutable::Vector[5, 6, 7]]]
|
1128
|
+
# v.update_in(3, "a", 1) { |value| value + 9 }
|
1129
|
+
# # => Immutable::Vector[123, 456, 789, Immutable::Hash["a" => Immutable::Vector[5, 15, 7]]]
|
1130
|
+
#
|
1131
|
+
# @param key_path [Object(s)] List of keys which form the path to the key to be modified
|
1132
|
+
# @yield [value] The previously stored value
|
1133
|
+
# @yieldreturn [Object] The new value to store
|
1134
|
+
# @return [Vector]
|
1135
|
+
def update_in(*key_path, &block)
|
1136
|
+
if key_path.empty?
|
1137
|
+
raise ArgumentError, 'must have at least one key in path'
|
1138
|
+
end
|
1139
|
+
key = key_path[0]
|
1140
|
+
if key_path.size == 1
|
1141
|
+
new_value = block.call(get(key))
|
1142
|
+
else
|
1143
|
+
value = fetch(key, Immutable::EmptyHash)
|
1144
|
+
new_value = value.update_in(*key_path[1..-1], &block)
|
1145
|
+
end
|
1146
|
+
set(key, new_value)
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
# Retrieve the item at `index`. If there is none (either the provided index
|
1150
|
+
# is too high or too low), return `nil`.
|
1151
|
+
#
|
1152
|
+
# @example
|
1153
|
+
# v = Immutable::Vector["A", "B", "C", "D"]
|
1154
|
+
# v.get(2) # => "C"
|
1155
|
+
# v.get(-1) # => "D"
|
1156
|
+
# v.get(4) # => nil
|
1157
|
+
#
|
1158
|
+
# @param index [Integer] The index to retrieve
|
1159
|
+
# @return [Object]
|
1160
|
+
def get(index)
|
1161
|
+
return nil if @size == 0
|
1162
|
+
index += @size if index < 0
|
1163
|
+
return nil if index >= @size || index < 0
|
1164
|
+
leaf_node_for(@root, @levels * BITS_PER_LEVEL, index)[index & INDEX_MASK]
|
1165
|
+
end
|
1166
|
+
alias at get
|
1167
|
+
|
1168
|
+
# Retrieve the value at `index` with optional default.
|
1169
|
+
#
|
1170
|
+
# @overload fetch(index)
|
1171
|
+
# Retrieve the value at the given index, or raise an `IndexError` if not
|
1172
|
+
# found.
|
1173
|
+
#
|
1174
|
+
# @param index [Integer] The index to look up
|
1175
|
+
# @raise [IndexError] if index does not exist
|
1176
|
+
# @example
|
1177
|
+
# v = Immutable::Vector["A", "B", "C", "D"]
|
1178
|
+
# v.fetch(2) # => "C"
|
1179
|
+
# v.fetch(-1) # => "D"
|
1180
|
+
# v.fetch(4) # => IndexError: index 4 outside of vector bounds
|
1181
|
+
#
|
1182
|
+
# @overload fetch(index) { |index| ... }
|
1183
|
+
# Retrieve the value at the given index, or return the result of yielding
|
1184
|
+
# the block if not found.
|
1185
|
+
#
|
1186
|
+
# @yield Once if the index is not found.
|
1187
|
+
# @yieldparam [Integer] index The index which does not exist
|
1188
|
+
# @yieldreturn [Object] Default value to return
|
1189
|
+
# @param index [Integer] The index to look up
|
1190
|
+
# @example
|
1191
|
+
# v = Immutable::Vector["A", "B", "C", "D"]
|
1192
|
+
# v.fetch(2) { |i| i * i } # => "C"
|
1193
|
+
# v.fetch(4) { |i| i * i } # => 16
|
1194
|
+
#
|
1195
|
+
# @overload fetch(index, default)
|
1196
|
+
# Retrieve the value at the given index, or return the provided `default`
|
1197
|
+
# value if not found.
|
1198
|
+
#
|
1199
|
+
# @param index [Integer] The index to look up
|
1200
|
+
# @param default [Object] Object to return if the key is not found
|
1201
|
+
# @example
|
1202
|
+
# v = Immutable::Vector["A", "B", "C", "D"]
|
1203
|
+
# v.fetch(2, "Z") # => "C"
|
1204
|
+
# v.fetch(4, "Z") # => "Z"
|
1205
|
+
#
|
1206
|
+
# @return [Object]
|
1207
|
+
def fetch(index, default = (missing_default = true))
|
1208
|
+
if index >= -@size && index < @size
|
1209
|
+
get(index)
|
1210
|
+
elsif block_given?
|
1211
|
+
yield(index)
|
1212
|
+
elsif !missing_default
|
1213
|
+
default
|
1214
|
+
else
|
1215
|
+
raise IndexError, "index #{index} outside of vector bounds"
|
1216
|
+
end
|
1217
|
+
end
|
1218
|
+
|
1219
|
+
# Return the value of successively indexing into a nested collection.
|
1220
|
+
# If any of the keys is not present, return `nil`.
|
1221
|
+
#
|
1222
|
+
# @example
|
1223
|
+
# v = Immutable::Vector[9, Immutable::Hash[c: 'a', d: 4]]
|
1224
|
+
# v.dig(1, :c) # => "a"
|
1225
|
+
# v.dig(1, :f) # => nil
|
1226
|
+
#
|
1227
|
+
# @return [Object]
|
1228
|
+
def dig(key, *rest)
|
1229
|
+
value = self[key]
|
1230
|
+
if rest.empty? || value.nil?
|
1231
|
+
value
|
1232
|
+
else
|
1233
|
+
value.dig(*rest)
|
1234
|
+
end
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
# Return specific objects from the `Vector`. All overloads return `nil` if
|
1238
|
+
# the starting index is out of range.
|
1239
|
+
#
|
1240
|
+
# @overload vector.slice(index)
|
1241
|
+
# Returns a single object at the given `index`. If `index` is negative,
|
1242
|
+
# count backwards from the end.
|
1243
|
+
#
|
1244
|
+
# @param index [Integer] The index to retrieve. May be negative.
|
1245
|
+
# @return [Object]
|
1246
|
+
# @example
|
1247
|
+
# v = Immutable::Vector["A", "B", "C", "D", "E", "F"]
|
1248
|
+
# v[2] # => "C"
|
1249
|
+
# v[-1] # => "F"
|
1250
|
+
# v[6] # => nil
|
1251
|
+
#
|
1252
|
+
# @overload vector.slice(index, length)
|
1253
|
+
# Return a subvector starting at `index` and continuing for `length`
|
1254
|
+
# elements or until the end of the `Vector`, whichever occurs first.
|
1255
|
+
#
|
1256
|
+
# @param start [Integer] The index to start retrieving items from. May be
|
1257
|
+
# negative.
|
1258
|
+
# @param length [Integer] The number of items to retrieve.
|
1259
|
+
# @return [Vector]
|
1260
|
+
# @example
|
1261
|
+
# v = Immutable::Vector["A", "B", "C", "D", "E", "F"]
|
1262
|
+
# v[2, 3] # => Immutable::Vector["C", "D", "E"]
|
1263
|
+
# v[-2, 3] # => Immutable::Vector["E", "F"]
|
1264
|
+
# v[20, 1] # => nil
|
1265
|
+
#
|
1266
|
+
# @overload vector.slice(index..end)
|
1267
|
+
# Return a subvector starting at `index` and continuing to index
|
1268
|
+
# `end` or the end of the `Vector`, whichever occurs first.
|
1269
|
+
#
|
1270
|
+
# @param range [Range] The range of indices to retrieve.
|
1271
|
+
# @return [Vector]
|
1272
|
+
# @example
|
1273
|
+
# v = Immutable::Vector["A", "B", "C", "D", "E", "F"]
|
1274
|
+
# v[2..3] # => Immutable::Vector["C", "D"]
|
1275
|
+
# v[-2..100] # => Immutable::Vector["E", "F"]
|
1276
|
+
# v[20..21] # => nil
|
1277
|
+
def slice(arg, length = (missing_length = true))
|
1278
|
+
if missing_length
|
1279
|
+
if arg.is_a?(Range)
|
1280
|
+
from, to = arg.begin, arg.end
|
1281
|
+
from += @size if from < 0
|
1282
|
+
to += @size if to < 0
|
1283
|
+
to += 1 if !arg.exclude_end?
|
1284
|
+
length = to - from
|
1285
|
+
length = 0 if length < 0
|
1286
|
+
subsequence(from, length)
|
1287
|
+
else
|
1288
|
+
get(arg)
|
1289
|
+
end
|
1290
|
+
else
|
1291
|
+
arg += @size if arg < 0
|
1292
|
+
subsequence(arg, length)
|
1293
|
+
end
|
1294
|
+
end
|
1295
|
+
alias [] slice
|
1296
|
+
|
1297
|
+
# Return a new `Vector` with the given values inserted before the element
|
1298
|
+
# at `index`. If `index` is greater than the current length, `nil` values
|
1299
|
+
# are added to pad the `Vector` to the required size.
|
1300
|
+
#
|
1301
|
+
# @example
|
1302
|
+
# Immutable::Vector["A", "B", "C", "D"].insert(2, "X", "Y", "Z")
|
1303
|
+
# # => Immutable::Vector["A", "B", "X", "Y", "Z", "C", "D"]
|
1304
|
+
# Immutable::Vector[].insert(2, "X", "Y", "Z")
|
1305
|
+
# # => Immutable::Vector[nil, nil, "X", "Y", "Z"]
|
1306
|
+
#
|
1307
|
+
# @param index [Integer] The index where the new items should go
|
1308
|
+
# @param items [Array] The items to add
|
1309
|
+
# @return [Vector]
|
1310
|
+
# @raise [IndexError] if index exceeds negative range.
|
1311
|
+
def insert(index, *items)
|
1312
|
+
raise IndexError if index < -@size
|
1313
|
+
index += @size if index < 0
|
1314
|
+
|
1315
|
+
if index < @size
|
1316
|
+
suffix = flatten_suffix(@root, @levels * BITS_PER_LEVEL, index, [])
|
1317
|
+
suffix.unshift(*items)
|
1318
|
+
elsif index == @size
|
1319
|
+
suffix = items
|
1320
|
+
else
|
1321
|
+
suffix = Array.new(index - @size, nil).concat(items)
|
1322
|
+
index = @size
|
1323
|
+
end
|
1324
|
+
|
1325
|
+
replace_suffix(index, suffix)
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
# Return a new `Vector` with the element at `index` removed. If the given `index`
|
1329
|
+
# does not exist, return `self`.
|
1330
|
+
#
|
1331
|
+
# @example
|
1332
|
+
# Immutable::Vector["A", "B", "C", "D"].delete_at(2)
|
1333
|
+
# # => Immutable::Vector["A", "B", "D"]
|
1334
|
+
#
|
1335
|
+
# @param index [Integer] The index to remove
|
1336
|
+
# @return [Vector]
|
1337
|
+
def delete_at(index)
|
1338
|
+
return self if index >= @size || index < -@size
|
1339
|
+
index += @size if index < 0
|
1340
|
+
|
1341
|
+
suffix = flatten_suffix(@root, @levels * BITS_PER_LEVEL, index, [])
|
1342
|
+
replace_suffix(index, suffix.tap(&:shift))
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
# Return a new `Vector` with the last element removed. Return `self` if
|
1346
|
+
# empty.
|
1347
|
+
#
|
1348
|
+
# @example
|
1349
|
+
# Immutable::Vector["A", "B", "C"].pop # => Immutable::Vector["A", "B"]
|
1350
|
+
#
|
1351
|
+
# @return [Vector]
|
1352
|
+
def pop
|
1353
|
+
return self if @size == 0
|
1354
|
+
replace_suffix(@size-1, [])
|
1355
|
+
end
|
1356
|
+
|
1357
|
+
# Return a new `Vector` with `object` inserted before the first element,
|
1358
|
+
# moving the other elements upwards.
|
1359
|
+
#
|
1360
|
+
# @example
|
1361
|
+
# Immutable::Vector["A", "B"].unshift("Z")
|
1362
|
+
# # => Immutable::Vector["Z", "A", "B"]
|
1363
|
+
#
|
1364
|
+
# @param object [Object] The value to prepend
|
1365
|
+
# @return [Vector]
|
1366
|
+
def unshift(object)
|
1367
|
+
insert(0, object)
|
1368
|
+
end
|
1369
|
+
|
1370
|
+
# Return a new `Vector` with the first element removed. If empty, return
|
1371
|
+
# `self`.
|
1372
|
+
#
|
1373
|
+
# @example
|
1374
|
+
# Immutable::Vector["A", "B", "C"].shift # => Immutable::Vector["B", "C"]
|
1375
|
+
#
|
1376
|
+
# @return [Vector]
|
1377
|
+
def shift
|
1378
|
+
delete_at(0)
|
1379
|
+
end
|
1380
|
+
|
1381
|
+
# Call the given block once for each item in the vector, passing each
|
1382
|
+
# item from first to last successively to the block. If no block is given,
|
1383
|
+
# an `Enumerator` is returned instead.
|
1384
|
+
#
|
1385
|
+
# @example
|
1386
|
+
# Immutable::Vector["A", "B", "C"].each { |e| puts "Element: #{e}" }
|
1387
|
+
#
|
1388
|
+
# Element: A
|
1389
|
+
# Element: B
|
1390
|
+
# Element: C
|
1391
|
+
# # => Immutable::Vector["A", "B", "C"]
|
1392
|
+
#
|
1393
|
+
# @return [self, Enumerator]
|
1394
|
+
def each(&block)
|
1395
|
+
return to_enum unless block_given?
|
1396
|
+
traverse_depth_first(@root, @levels, &block)
|
1397
|
+
self
|
1398
|
+
end
|
1399
|
+
|
1400
|
+
# Call the given block once for each item in the vector, from last to
|
1401
|
+
# first.
|
1402
|
+
#
|
1403
|
+
# @example
|
1404
|
+
# Immutable::Vector["A", "B", "C"].reverse_each { |e| puts "Element: #{e}" }
|
1405
|
+
#
|
1406
|
+
# Element: C
|
1407
|
+
# Element: B
|
1408
|
+
# Element: A
|
1409
|
+
#
|
1410
|
+
# @return [self]
|
1411
|
+
def reverse_each(&block)
|
1412
|
+
return enum_for(:reverse_each) unless block_given?
|
1413
|
+
reverse_traverse_depth_first(@root, @levels, &block)
|
1414
|
+
self
|
1415
|
+
end
|
1416
|
+
|
1417
|
+
# Return a new `Vector` containing all elements for which the given block returns
|
1418
|
+
# true.
|
1419
|
+
#
|
1420
|
+
# @example
|
1421
|
+
# Immutable::Vector["Bird", "Cow", "Elephant"].select { |e| e.size >= 4 }
|
1422
|
+
# # => Immutable::Vector["Bird", "Elephant"]
|
1423
|
+
#
|
1424
|
+
# @return [Vector]
|
1425
|
+
# @yield [element] Once for each element.
|
1426
|
+
def select
|
1427
|
+
return enum_for(:select) unless block_given?
|
1428
|
+
reduce(self.class.empty) { |vector, item| yield(item) ? vector.add(item) : vector }
|
1429
|
+
end
|
1430
|
+
alias find_all select
|
1431
|
+
alias keep_if select
|
1432
|
+
|
1433
|
+
# Return a new `Vector` with all items which are equal to `obj` removed.
|
1434
|
+
# `#==` is used for checking equality.
|
1435
|
+
#
|
1436
|
+
# @example
|
1437
|
+
# Immutable::Vector["C", "B", "A", "B"].delete("B") # => Immutable::Vector["C", "A"]
|
1438
|
+
#
|
1439
|
+
# @param obj [Object] The object to remove (every occurrence)
|
1440
|
+
# @return [Vector]
|
1441
|
+
def delete(obj)
|
1442
|
+
select { |item| item != obj }
|
1443
|
+
end
|
1444
|
+
|
1445
|
+
# Invoke the given block once for each item in the vector, and return a new
|
1446
|
+
# `Vector` containing the values returned by the block. If no block is
|
1447
|
+
# provided, return an enumerator.
|
1448
|
+
#
|
1449
|
+
# @example
|
1450
|
+
# Immutable::Vector[3, 2, 1].map { |e| e * e } # => Immutable::Vector[9, 4, 1]
|
1451
|
+
#
|
1452
|
+
# @return [Vector, Enumerator]
|
1453
|
+
def map
|
1454
|
+
return enum_for(:map) if not block_given?
|
1455
|
+
return self if empty?
|
1456
|
+
self.class.new(super)
|
1457
|
+
end
|
1458
|
+
alias collect map
|
1459
|
+
|
1460
|
+
# Return a new `Vector` with the concatenated results of running the block once
|
1461
|
+
# for every element in this `Vector`.
|
1462
|
+
#
|
1463
|
+
# @example
|
1464
|
+
# Immutable::Vector[1, 2, 3].flat_map { |x| [x, -x] }
|
1465
|
+
# # => Immutable::Vector[1, -1, 2, -2, 3, -3]
|
1466
|
+
#
|
1467
|
+
# @return [Vector]
|
1468
|
+
def flat_map
|
1469
|
+
return enum_for(:flat_map) if not block_given?
|
1470
|
+
return self if empty?
|
1471
|
+
self.class.new(super)
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
# Return a new `Vector` with the same elements as this one, but randomly permuted.
|
1475
|
+
#
|
1476
|
+
# @example
|
1477
|
+
# Immutable::Vector[1, 2, 3, 4].shuffle # => Immutable::Vector[4, 1, 3, 2]
|
1478
|
+
#
|
1479
|
+
# @return [Vector]
|
1480
|
+
def shuffle
|
1481
|
+
self.class.new(((array = to_a).frozen? ? array.shuffle : array.shuffle!).freeze)
|
1482
|
+
end
|
1483
|
+
|
1484
|
+
# Return a new `Vector` with no duplicate elements, as determined by `#hash` and
|
1485
|
+
# `#eql?`. For each group of equivalent elements, only the first will be retained.
|
1486
|
+
#
|
1487
|
+
# @example
|
1488
|
+
# Immutable::Vector["A", "B", "C", "B"].uniq # => Immutable::Vector["A", "B", "C"]
|
1489
|
+
# Immutable::Vector["a", "A", "b"].uniq(&:upcase) # => Immutable::Vector["a", "b"]
|
1490
|
+
#
|
1491
|
+
# @return [Vector]
|
1492
|
+
def uniq(&block)
|
1493
|
+
array = to_a
|
1494
|
+
if array.frozen?
|
1495
|
+
self.class.new(array.uniq(&block).freeze)
|
1496
|
+
elsif array.uniq!(&block) # returns nil if no changes were made
|
1497
|
+
self.class.new(array.freeze)
|
1498
|
+
else
|
1499
|
+
self
|
1500
|
+
end
|
1501
|
+
end
|
1502
|
+
|
1503
|
+
# Return a new `Vector` with the same elements as this one, but in reverse order.
|
1504
|
+
#
|
1505
|
+
# @example
|
1506
|
+
# Immutable::Vector["A", "B", "C"].reverse # => Immutable::Vector["C", "B", "A"]
|
1507
|
+
#
|
1508
|
+
# @return [Vector]
|
1509
|
+
def reverse
|
1510
|
+
self.class.new(((array = to_a).frozen? ? array.reverse : array.reverse!).freeze)
|
1511
|
+
end
|
1512
|
+
|
1513
|
+
# Return a new `Vector` with the same elements, but rotated so that the one at
|
1514
|
+
# index `count` is the first element of the new vector. If `count` is positive,
|
1515
|
+
# the elements will be shifted left, and those shifted past the lowest position
|
1516
|
+
# will be moved to the end. If `count` is negative, the elements will be shifted
|
1517
|
+
# right, and those shifted past the last position will be moved to the beginning.
|
1518
|
+
#
|
1519
|
+
# @example
|
1520
|
+
# v = Immutable::Vector["A", "B", "C", "D", "E", "F"]
|
1521
|
+
# v.rotate(2) # => Immutable::Vector["C", "D", "E", "F", "A", "B"]
|
1522
|
+
# v.rotate(-1) # => Immutable::Vector["F", "A", "B", "C", "D", "E"]
|
1523
|
+
#
|
1524
|
+
# @param count [Integer] The number of positions to shift items by
|
1525
|
+
# @return [Vector]
|
1526
|
+
def rotate(count = 1)
|
1527
|
+
return self if (count % @size) == 0
|
1528
|
+
self.class.new(((array = to_a).frozen? ? array.rotate(count) : array.rotate!(count)).freeze)
|
1529
|
+
end
|
1530
|
+
|
1531
|
+
# Return a new `Vector` with all nested vectors and arrays recursively "flattened
|
1532
|
+
# out". That is, their elements inserted into the new `Vector` in the place where
|
1533
|
+
# the nested array/vector originally was. If an optional `level` argument is
|
1534
|
+
# provided, the flattening will only be done recursively that number of times.
|
1535
|
+
# A `level` of 0 means not to flatten at all, 1 means to only flatten nested
|
1536
|
+
# arrays/vectors which are directly contained within this `Vector`.
|
1537
|
+
#
|
1538
|
+
# @example
|
1539
|
+
# v = Immutable::Vector["A", Immutable::Vector["B", "C", Immutable::Vector["D"]]]
|
1540
|
+
# v.flatten(1)
|
1541
|
+
# # => Immutable::Vector["A", "B", "C", Immutable::Vector["D"]]
|
1542
|
+
# v.flatten
|
1543
|
+
# # => Immutable::Vector["A", "B", "C", "D"]
|
1544
|
+
#
|
1545
|
+
# @param level [Integer] The depth to which flattening should be applied
|
1546
|
+
# @return [Vector]
|
1547
|
+
def flatten(level = -1)
|
1548
|
+
return self if level == 0
|
1549
|
+
array = to_a
|
1550
|
+
if array.frozen?
|
1551
|
+
self.class.new(array.flatten(level).freeze)
|
1552
|
+
elsif array.flatten!(level) # returns nil if no changes were made
|
1553
|
+
self.class.new(array.freeze)
|
1554
|
+
else
|
1555
|
+
self
|
1556
|
+
end
|
1557
|
+
end
|
1558
|
+
|
1559
|
+
# Return a new `Vector` built by concatenating this one with `other`. `other`
|
1560
|
+
# can be any object which is convertible to an `Array` using `#to_a`.
|
1561
|
+
#
|
1562
|
+
# @example
|
1563
|
+
# Immutable::Vector["A", "B", "C"] + ["D", "E"]
|
1564
|
+
# # => Immutable::Vector["A", "B", "C", "D", "E"]
|
1565
|
+
#
|
1566
|
+
# @param other [Enumerable] The collection to concatenate onto this vector
|
1567
|
+
# @return [Vector]
|
1568
|
+
def +(other)
|
1569
|
+
other = other.to_a
|
1570
|
+
other = other.dup if other.frozen?
|
1571
|
+
replace_suffix(@size, other)
|
1572
|
+
end
|
1573
|
+
alias concat +
|
1574
|
+
|
1575
|
+
# Combine two vectors by "zipping" them together. `others` should be arrays
|
1576
|
+
# and/or vectors. The corresponding elements from this `Vector` and each of
|
1577
|
+
# `others` (that is, the elements with the same indices) will be gathered
|
1578
|
+
# into arrays.
|
1579
|
+
#
|
1580
|
+
# If `others` contains fewer elements than this vector, `nil` will be used
|
1581
|
+
# for padding.
|
1582
|
+
#
|
1583
|
+
# @overload zip(*others)
|
1584
|
+
# Return a new vector containing the new arrays.
|
1585
|
+
#
|
1586
|
+
# @return [Vector]
|
1587
|
+
#
|
1588
|
+
# @overload zip(*others)
|
1589
|
+
# @yield [pair] once for each array
|
1590
|
+
# @return [nil]
|
1591
|
+
#
|
1592
|
+
# @example
|
1593
|
+
# v1 = Immutable::Vector["A", "B", "C"]
|
1594
|
+
# v2 = Immutable::Vector[1, 2]
|
1595
|
+
# v1.zip(v2)
|
1596
|
+
# # => Immutable::Vector[["A", 1], ["B", 2], ["C", nil]]
|
1597
|
+
#
|
1598
|
+
# @param others [Array] The arrays/vectors to zip together with this one
|
1599
|
+
# @return [Vector]
|
1600
|
+
def zip(*others)
|
1601
|
+
if block_given?
|
1602
|
+
super
|
1603
|
+
else
|
1604
|
+
self.class.new(super)
|
1605
|
+
end
|
1606
|
+
end
|
1607
|
+
|
1608
|
+
# Return a new `Vector` with the same items, but sorted.
|
1609
|
+
#
|
1610
|
+
# @overload sort
|
1611
|
+
# Compare elements with their natural sort key (`#<=>`).
|
1612
|
+
#
|
1613
|
+
# @example
|
1614
|
+
# Immutable::Vector["Elephant", "Dog", "Lion"].sort
|
1615
|
+
# # => Immutable::Vector["Dog", "Elephant", "Lion"]
|
1616
|
+
#
|
1617
|
+
# @overload sort
|
1618
|
+
# Uses the block as a comparator to determine sorted order.
|
1619
|
+
#
|
1620
|
+
# @yield [a, b] Any number of times with different pairs of elements.
|
1621
|
+
# @yieldreturn [Integer] Negative if the first element should be sorted
|
1622
|
+
# lower, positive if the latter element, or 0 if
|
1623
|
+
# equal.
|
1624
|
+
# @example
|
1625
|
+
# Immutable::Vector["Elephant", "Dog", "Lion"].sort { |a,b| a.size <=> b.size }
|
1626
|
+
# # => Immutable::Vector["Dog", "Lion", "Elephant"]
|
1627
|
+
#
|
1628
|
+
# @return [Vector]
|
1629
|
+
def sort
|
1630
|
+
self.class.new(super)
|
1631
|
+
end
|
1632
|
+
|
1633
|
+
# Return a new `Vector` with the same items, but sorted. The sort order is
|
1634
|
+
# determined by mapping the items through the given block to obtain sort
|
1635
|
+
# keys, and then sorting the keys according to their natural sort order
|
1636
|
+
# (`#<=>`).
|
1637
|
+
#
|
1638
|
+
# @yield [element] Once for each element.
|
1639
|
+
# @yieldreturn a sort key object for the yielded element.
|
1640
|
+
# @example
|
1641
|
+
# Immutable::Vector["Elephant", "Dog", "Lion"].sort_by { |e| e.size }
|
1642
|
+
# # => Immutable::Vector["Dog", "Lion", "Elephant"]
|
1643
|
+
#
|
1644
|
+
# @return [Vector]
|
1645
|
+
def sort_by
|
1646
|
+
self.class.new(super)
|
1647
|
+
end
|
1648
|
+
|
1649
|
+
# Drop the first `n` elements and return the rest in a new `Vector`.
|
1650
|
+
#
|
1651
|
+
# @example
|
1652
|
+
# Immutable::Vector["A", "B", "C", "D", "E", "F"].drop(2)
|
1653
|
+
# # => Immutable::Vector["C", "D", "E", "F"]
|
1654
|
+
#
|
1655
|
+
# @param n [Integer] The number of elements to remove
|
1656
|
+
# @return [Vector]
|
1657
|
+
# @raise ArgumentError if `n` is negative.
|
1658
|
+
def drop(n)
|
1659
|
+
return self if n == 0
|
1660
|
+
return self.class.empty if n >= @size
|
1661
|
+
raise ArgumentError, 'attempt to drop negative size' if n < 0
|
1662
|
+
self.class.new(flatten_suffix(@root, @levels * BITS_PER_LEVEL, n, []))
|
1663
|
+
end
|
1664
|
+
|
1665
|
+
# Return only the first `n` elements in a new `Vector`.
|
1666
|
+
#
|
1667
|
+
# @example
|
1668
|
+
# Immutable::Vector["A", "B", "C", "D", "E", "F"].take(4)
|
1669
|
+
# # => Immutable::Vector["A", "B", "C", "D"]
|
1670
|
+
#
|
1671
|
+
# @param n [Integer] The number of elements to retain
|
1672
|
+
# @return [Vector]
|
1673
|
+
def take(n)
|
1674
|
+
return self if n >= @size
|
1675
|
+
self.class.new(super)
|
1676
|
+
end
|
1677
|
+
|
1678
|
+
# Drop elements up to, but not including, the first element for which the
|
1679
|
+
# block returns `nil` or `false`. Gather the remaining elements into a new
|
1680
|
+
# `Vector`. If no block is given, an `Enumerator` is returned instead.
|
1681
|
+
#
|
1682
|
+
# @example
|
1683
|
+
# Immutable::Vector[1, 3, 5, 7, 6, 4, 2].drop_while { |e| e < 5 }
|
1684
|
+
# # => Immutable::Vector[5, 7, 6, 4, 2]
|
1685
|
+
#
|
1686
|
+
# @return [Vector, Enumerator]
|
1687
|
+
def drop_while
|
1688
|
+
return enum_for(:drop_while) if not block_given?
|
1689
|
+
self.class.new(super)
|
1690
|
+
end
|
1691
|
+
|
1692
|
+
# Gather elements up to, but not including, the first element for which the
|
1693
|
+
# block returns `nil` or `false`, and return them in a new `Vector`. If no block
|
1694
|
+
# is given, an `Enumerator` is returned instead.
|
1695
|
+
#
|
1696
|
+
# @example
|
1697
|
+
# Immutable::Vector[1, 3, 5, 7, 6, 4, 2].take_while { |e| e < 5 }
|
1698
|
+
# # => Immutable::Vector[1, 3]
|
1699
|
+
#
|
1700
|
+
# @return [Vector, Enumerator]
|
1701
|
+
def take_while
|
1702
|
+
return enum_for(:take_while) if not block_given?
|
1703
|
+
self.class.new(super)
|
1704
|
+
end
|
1705
|
+
|
1706
|
+
# Repetition. Return a new `Vector` built by concatenating `times` copies
|
1707
|
+
# of this one together.
|
1708
|
+
#
|
1709
|
+
# @example
|
1710
|
+
# Immutable::Vector["A", "B"] * 3
|
1711
|
+
# # => Immutable::Vector["A", "B", "A", "B", "A", "B"]
|
1712
|
+
#
|
1713
|
+
# @param times [Integer] The number of times to repeat the elements in this vector
|
1714
|
+
# @return [Vector]
|
1715
|
+
def *(times)
|
1716
|
+
return self.class.empty if times == 0
|
1717
|
+
return self if times == 1
|
1718
|
+
result = (to_a * times)
|
1719
|
+
result.is_a?(Array) ? self.class.new(result) : result
|
1720
|
+
end
|
1721
|
+
|
1722
|
+
# Replace a range of indexes with the given object.
|
1723
|
+
#
|
1724
|
+
# @overload fill(object)
|
1725
|
+
# Return a new `Vector` of the same size, with every index set to
|
1726
|
+
# `object`.
|
1727
|
+
#
|
1728
|
+
# @param [Object] object Fill value.
|
1729
|
+
# @example
|
1730
|
+
# Immutable::Vector["A", "B", "C", "D", "E", "F"].fill("Z")
|
1731
|
+
# # => Immutable::Vector["Z", "Z", "Z", "Z", "Z", "Z"]
|
1732
|
+
#
|
1733
|
+
# @overload fill(object, index)
|
1734
|
+
# Return a new `Vector` with all indexes from `index` to the end of the
|
1735
|
+
# vector set to `object`.
|
1736
|
+
#
|
1737
|
+
# @param [Object] object Fill value.
|
1738
|
+
# @param [Integer] index Starting index. May be negative.
|
1739
|
+
# @example
|
1740
|
+
# Immutable::Vector["A", "B", "C", "D", "E", "F"].fill("Z", 3)
|
1741
|
+
# # => Immutable::Vector["A", "B", "C", "Z", "Z", "Z"]
|
1742
|
+
#
|
1743
|
+
# @overload fill(object, index, length)
|
1744
|
+
# Return a new `Vector` with `length` indexes, beginning from `index`,
|
1745
|
+
# set to `object`. Expands the `Vector` if `length` would extend beyond
|
1746
|
+
# the current length.
|
1747
|
+
#
|
1748
|
+
# @param [Object] object Fill value.
|
1749
|
+
# @param [Integer] index Starting index. May be negative.
|
1750
|
+
# @param [Integer] length
|
1751
|
+
# @example
|
1752
|
+
# Immutable::Vector["A", "B", "C", "D", "E", "F"].fill("Z", 3, 2)
|
1753
|
+
# # => Immutable::Vector["A", "B", "C", "Z", "Z", "F"]
|
1754
|
+
# Immutable::Vector["A", "B"].fill("Z", 1, 5)
|
1755
|
+
# # => Immutable::Vector["A", "Z", "Z", "Z", "Z", "Z"]
|
1756
|
+
#
|
1757
|
+
# @return [Vector]
|
1758
|
+
# @raise [IndexError] if index is out of negative range.
|
1759
|
+
def fill(object, index = 0, length = nil)
|
1760
|
+
raise IndexError if index < -@size
|
1761
|
+
index += @size if index < 0
|
1762
|
+
length ||= @size - index # to the end of the array, if no length given
|
1763
|
+
|
1764
|
+
if index < @size
|
1765
|
+
suffix = flatten_suffix(@root, @levels * BITS_PER_LEVEL, index, [])
|
1766
|
+
suffix.fill(object, 0, length)
|
1767
|
+
elsif index == @size
|
1768
|
+
suffix = Array.new(length, object)
|
1769
|
+
else
|
1770
|
+
suffix = Array.new(index - @size, nil).concat(Array.new(length, object))
|
1771
|
+
index = @size
|
1772
|
+
end
|
1773
|
+
|
1774
|
+
replace_suffix(index, suffix)
|
1775
|
+
end
|
1776
|
+
|
1777
|
+
# When invoked with a block, yields all combinations of length `n` of items
|
1778
|
+
# from the `Vector`, and then returns `self`. There is no guarantee about
|
1779
|
+
# which order the combinations will be yielded.
|
1780
|
+
#
|
1781
|
+
# If no block is given, an `Enumerator` is returned instead.
|
1782
|
+
#
|
1783
|
+
# @example
|
1784
|
+
# v = Immutable::Vector[5, 6, 7, 8]
|
1785
|
+
# v.combination(3) { |c| puts "Combination: #{c}" }
|
1786
|
+
#
|
1787
|
+
# Combination: [5, 6, 7]
|
1788
|
+
# Combination: [5, 6, 8]
|
1789
|
+
# Combination: [5, 7, 8]
|
1790
|
+
# Combination: [6, 7, 8]
|
1791
|
+
# #=> Immutable::Vector[5, 6, 7, 8]
|
1792
|
+
#
|
1793
|
+
# @return [self, Enumerator]
|
1794
|
+
def combination(n)
|
1795
|
+
return enum_for(:combination, n) if not block_given?
|
1796
|
+
return self if n < 0 || @size < n
|
1797
|
+
if n == 0
|
1798
|
+
yield []
|
1799
|
+
elsif n == 1
|
1800
|
+
each { |item| yield [item] }
|
1801
|
+
elsif n == @size
|
1802
|
+
yield to_a
|
1803
|
+
else
|
1804
|
+
combos = lambda do |result,index,remaining|
|
1805
|
+
while @size - index > remaining
|
1806
|
+
if remaining == 1
|
1807
|
+
yield result.dup << get(index)
|
1808
|
+
else
|
1809
|
+
combos[result.dup << get(index), index+1, remaining-1]
|
1810
|
+
end
|
1811
|
+
index += 1
|
1812
|
+
end
|
1813
|
+
index.upto(@size-1) { |i| result << get(i) }
|
1814
|
+
yield result
|
1815
|
+
end
|
1816
|
+
combos[[], 0, n]
|
1817
|
+
end
|
1818
|
+
self
|
1819
|
+
end
|
1820
|
+
|
1821
|
+
# When invoked with a block, yields all repeated combinations of length `n` of
|
1822
|
+
# items from the `Vector`, and then returns `self`. A "repeated combination" is
|
1823
|
+
# one in which any item from the `Vector` can appear consecutively any number of
|
1824
|
+
# times.
|
1825
|
+
#
|
1826
|
+
# There is no guarantee about which order the combinations will be yielded in.
|
1827
|
+
#
|
1828
|
+
# If no block is given, an `Enumerator` is returned instead.
|
1829
|
+
#
|
1830
|
+
# @example
|
1831
|
+
# v = Immutable::Vector[5, 6, 7, 8]
|
1832
|
+
# v.repeated_combination(2) { |c| puts "Combination: #{c}" }
|
1833
|
+
#
|
1834
|
+
# Combination: [5, 5]
|
1835
|
+
# Combination: [5, 6]
|
1836
|
+
# Combination: [5, 7]
|
1837
|
+
# Combination: [5, 8]
|
1838
|
+
# Combination: [6, 6]
|
1839
|
+
# Combination: [6, 7]
|
1840
|
+
# Combination: [6, 8]
|
1841
|
+
# Combination: [7, 7]
|
1842
|
+
# Combination: [7, 8]
|
1843
|
+
# Combination: [8, 8]
|
1844
|
+
# # => Immutable::Vector[5, 6, 7, 8]
|
1845
|
+
#
|
1846
|
+
# @return [self, Enumerator]
|
1847
|
+
def repeated_combination(n)
|
1848
|
+
return enum_for(:repeated_combination, n) if not block_given?
|
1849
|
+
if n < 0
|
1850
|
+
# yield nothing
|
1851
|
+
elsif n == 0
|
1852
|
+
yield []
|
1853
|
+
elsif n == 1
|
1854
|
+
each { |item| yield [item] }
|
1855
|
+
elsif @size == 0
|
1856
|
+
# yield nothing
|
1857
|
+
else
|
1858
|
+
combos = lambda do |result,index,remaining|
|
1859
|
+
while index < @size-1
|
1860
|
+
if remaining == 1
|
1861
|
+
yield result.dup << get(index)
|
1862
|
+
else
|
1863
|
+
combos[result.dup << get(index), index, remaining-1]
|
1864
|
+
end
|
1865
|
+
index += 1
|
1866
|
+
end
|
1867
|
+
item = get(index)
|
1868
|
+
remaining.times { result << item }
|
1869
|
+
yield result
|
1870
|
+
end
|
1871
|
+
combos[[], 0, n]
|
1872
|
+
end
|
1873
|
+
self
|
1874
|
+
end
|
1875
|
+
|
1876
|
+
# Yields all permutations of length `n` of items from the `Vector`, and then
|
1877
|
+
# returns `self`. If no length `n` is specified, permutations of all elements
|
1878
|
+
# will be yielded.
|
1879
|
+
#
|
1880
|
+
# There is no guarantee about which order the permutations will be yielded in.
|
1881
|
+
#
|
1882
|
+
# If no block is given, an `Enumerator` is returned instead.
|
1883
|
+
#
|
1884
|
+
# @example
|
1885
|
+
# v = Immutable::Vector[5, 6, 7]
|
1886
|
+
# v.permutation(2) { |p| puts "Permutation: #{p}" }
|
1887
|
+
#
|
1888
|
+
# Permutation: [5, 6]
|
1889
|
+
# Permutation: [5, 7]
|
1890
|
+
# Permutation: [6, 5]
|
1891
|
+
# Permutation: [6, 7]
|
1892
|
+
# Permutation: [7, 5]
|
1893
|
+
# Permutation: [7, 6]
|
1894
|
+
# # => Immutable::Vector[5, 6, 7]
|
1895
|
+
#
|
1896
|
+
# @return [self, Enumerator]
|
1897
|
+
def permutation(n = @size)
|
1898
|
+
return enum_for(:permutation, n) if not block_given?
|
1899
|
+
if n < 0 || @size < n
|
1900
|
+
# yield nothing
|
1901
|
+
elsif n == 0
|
1902
|
+
yield []
|
1903
|
+
elsif n == 1
|
1904
|
+
each { |item| yield [item] }
|
1905
|
+
else
|
1906
|
+
used, result = [], []
|
1907
|
+
perms = lambda do |index|
|
1908
|
+
0.upto(@size-1) do |i|
|
1909
|
+
next if used[i]
|
1910
|
+
result[index] = get(i)
|
1911
|
+
if index < n-1
|
1912
|
+
used[i] = true
|
1913
|
+
perms[index+1]
|
1914
|
+
used[i] = false
|
1915
|
+
else
|
1916
|
+
yield result.dup
|
1917
|
+
end
|
1918
|
+
end
|
1919
|
+
end
|
1920
|
+
perms[0]
|
1921
|
+
end
|
1922
|
+
self
|
1923
|
+
end
|
1924
|
+
|
1925
|
+
# When invoked with a block, yields all repeated permutations of length `n` of
|
1926
|
+
# items from the `Vector`, and then returns `self`. A "repeated permutation" is
|
1927
|
+
# one where any item from the `Vector` can appear any number of times, and in
|
1928
|
+
# any position (not just consecutively)
|
1929
|
+
#
|
1930
|
+
# If no length `n` is specified, permutations of all elements will be yielded.
|
1931
|
+
# There is no guarantee about which order the permutations will be yielded in.
|
1932
|
+
#
|
1933
|
+
# If no block is given, an `Enumerator` is returned instead.
|
1934
|
+
#
|
1935
|
+
# @example
|
1936
|
+
# v = Immutable::Vector[5, 6, 7]
|
1937
|
+
# v.repeated_permutation(2) { |p| puts "Permutation: #{p}" }
|
1938
|
+
#
|
1939
|
+
# Permutation: [5, 5]
|
1940
|
+
# Permutation: [5, 6]
|
1941
|
+
# Permutation: [5, 7]
|
1942
|
+
# Permutation: [6, 5]
|
1943
|
+
# Permutation: [6, 6]
|
1944
|
+
# Permutation: [6, 7]
|
1945
|
+
# Permutation: [7, 5]
|
1946
|
+
# Permutation: [7, 6]
|
1947
|
+
# Permutation: [7, 7]
|
1948
|
+
# # => Immutable::Vector[5, 6, 7]
|
1949
|
+
#
|
1950
|
+
# @return [self, Enumerator]
|
1951
|
+
def repeated_permutation(n = @size)
|
1952
|
+
return enum_for(:repeated_permutation, n) if not block_given?
|
1953
|
+
if n < 0
|
1954
|
+
# yield nothing
|
1955
|
+
elsif n == 0
|
1956
|
+
yield []
|
1957
|
+
elsif n == 1
|
1958
|
+
each { |item| yield [item] }
|
1959
|
+
else
|
1960
|
+
result = []
|
1961
|
+
perms = lambda do |index|
|
1962
|
+
0.upto(@size-1) do |i|
|
1963
|
+
result[index] = get(i)
|
1964
|
+
if index < n-1
|
1965
|
+
perms[index+1]
|
1966
|
+
else
|
1967
|
+
yield result.dup
|
1968
|
+
end
|
1969
|
+
end
|
1970
|
+
end
|
1971
|
+
perms[0]
|
1972
|
+
end
|
1973
|
+
self
|
1974
|
+
end
|
1975
|
+
|
1976
|
+
# Cartesian product or multiplication.
|
1977
|
+
#
|
1978
|
+
# @overload product(*vectors)
|
1979
|
+
# Return a `Vector` of all combinations of elements from this `Vector` and each
|
1980
|
+
# of the given vectors or arrays. The length of the returned `Vector` is the product
|
1981
|
+
# of `self.size` and the size of each argument vector or array.
|
1982
|
+
# @example
|
1983
|
+
# v1 = Immutable::Vector[1, 2, 3]
|
1984
|
+
# v2 = Immutable::Vector["A", "B"]
|
1985
|
+
# v1.product(v2)
|
1986
|
+
# # => [[1, "A"], [1, "B"], [2, "A"], [2, "B"], [3, "A"], [3, "B"]]
|
1987
|
+
# @overload product
|
1988
|
+
# Return the result of multiplying all the items in this `Vector` together.
|
1989
|
+
#
|
1990
|
+
# @example
|
1991
|
+
# Immutable::Vector[1, 2, 3, 4, 5].product # => 120
|
1992
|
+
#
|
1993
|
+
# @return [Vector]
|
1994
|
+
def product(*vectors)
|
1995
|
+
# if no vectors passed, return "product" as in result of multiplying all items
|
1996
|
+
return super if vectors.empty?
|
1997
|
+
|
1998
|
+
vectors.unshift(self)
|
1999
|
+
|
2000
|
+
if vectors.any?(&:empty?)
|
2001
|
+
return block_given? ? self : []
|
2002
|
+
end
|
2003
|
+
|
2004
|
+
counters = Array.new(vectors.size, 0)
|
2005
|
+
|
2006
|
+
bump_counters = lambda do
|
2007
|
+
i = vectors.size-1
|
2008
|
+
counters[i] += 1
|
2009
|
+
while counters[i] == vectors[i].size
|
2010
|
+
counters[i] = 0
|
2011
|
+
i -= 1
|
2012
|
+
return true if i == -1 # we are done
|
2013
|
+
counters[i] += 1
|
2014
|
+
end
|
2015
|
+
false # not done yet
|
2016
|
+
end
|
2017
|
+
build_array = lambda do
|
2018
|
+
array = []
|
2019
|
+
counters.each_with_index { |index,i| array << vectors[i][index] }
|
2020
|
+
array
|
2021
|
+
end
|
2022
|
+
|
2023
|
+
if block_given?
|
2024
|
+
loop do
|
2025
|
+
yield build_array[]
|
2026
|
+
return self if bump_counters[]
|
2027
|
+
end
|
2028
|
+
else
|
2029
|
+
result = []
|
2030
|
+
loop do
|
2031
|
+
result << build_array[]
|
2032
|
+
return result if bump_counters[]
|
2033
|
+
end
|
2034
|
+
end
|
2035
|
+
end
|
2036
|
+
|
2037
|
+
# Assume all elements are vectors or arrays and transpose the rows and columns.
|
2038
|
+
# In other words, take the first element of each nested vector/array and gather
|
2039
|
+
# them together into a new `Vector`. Do likewise for the second, third, and so on
|
2040
|
+
# down to the end of each nested vector/array. Gather all the resulting `Vectors`
|
2041
|
+
# into a new `Vector` and return it.
|
2042
|
+
#
|
2043
|
+
# This operation is closely related to {#zip}. The result is almost the same as
|
2044
|
+
# calling {#zip} on the first nested vector/array with the others supplied as
|
2045
|
+
# arguments.
|
2046
|
+
#
|
2047
|
+
# @example
|
2048
|
+
# Immutable::Vector[["A", 10], ["B", 20], ["C", 30]].transpose
|
2049
|
+
# # => Immutable::Vector[Immutable::Vector["A", "B", "C"], Immutable::Vector[10, 20, 30]]
|
2050
|
+
#
|
2051
|
+
# @return [Vector]
|
2052
|
+
# @raise [IndexError] if elements are not of the same size.
|
2053
|
+
# @raise [TypeError] if an element does not respond to #size and #[]
|
2054
|
+
def transpose
|
2055
|
+
return self.class.empty if empty?
|
2056
|
+
result = Array.new(first.size) { [] }
|
2057
|
+
|
2058
|
+
0.upto(@size-1) do |i|
|
2059
|
+
source = get(i)
|
2060
|
+
if source.size != result.size
|
2061
|
+
raise IndexError, "element size differs (#{source.size} should be #{result.size})"
|
2062
|
+
end
|
2063
|
+
|
2064
|
+
0.upto(result.size-1) do |j|
|
2065
|
+
result[j].push(source[j])
|
2066
|
+
end
|
2067
|
+
end
|
2068
|
+
|
2069
|
+
result.map! { |a| self.class.new(a) }
|
2070
|
+
self.class.new(result)
|
2071
|
+
rescue NoMethodError
|
2072
|
+
if any? { |x| !x.respond_to?(:size) || !x.respond_to?(:[]) }
|
2073
|
+
bad = find { |x| !x.respond_to?(:size) || !x.respond_to?(:[]) }
|
2074
|
+
raise TypeError, "'#{bad.inspect}' must respond to #size and #[] to be transposed"
|
2075
|
+
else
|
2076
|
+
raise
|
2077
|
+
end
|
2078
|
+
end
|
2079
|
+
|
2080
|
+
# Finds a value from this `Vector` which meets the condition defined by the
|
2081
|
+
# provided block, using a binary search. The vector must already be sorted
|
2082
|
+
# with respect to the block. See Ruby's `Array#bsearch` for details,
|
2083
|
+
# behaviour is equivalent.
|
2084
|
+
#
|
2085
|
+
# @example
|
2086
|
+
# v = Immutable::Vector[1, 3, 5, 7, 9, 11, 13]
|
2087
|
+
# # Block returns true/false for exact element match:
|
2088
|
+
# v.bsearch { |e| e > 4 } # => 5
|
2089
|
+
# # Block returns number to match an element in 4 <= e <= 7:
|
2090
|
+
# v.bsearch { |e| 1 - e / 4 } # => 7
|
2091
|
+
#
|
2092
|
+
# @yield Once for at most `log n` elements, where `n` is the size of the
|
2093
|
+
# vector. The exact elements and ordering are undefined.
|
2094
|
+
# @yieldreturn [Boolean] `true` if this element matches the criteria, `false` otherwise.
|
2095
|
+
# @yieldreturn [Integer] See `Array#bsearch` for details.
|
2096
|
+
# @yieldparam [Object] element element to be evaluated
|
2097
|
+
# @return [Object] The matched element, or `nil` if none found.
|
2098
|
+
# @raise TypeError if the block returns a non-numeric, non-boolean, non-nil
|
2099
|
+
# value.
|
2100
|
+
def bsearch
|
2101
|
+
return enum_for(:bsearch) if not block_given?
|
2102
|
+
low, high, result = 0, @size, nil
|
2103
|
+
while low < high
|
2104
|
+
mid = (low + ((high - low) >> 1))
|
2105
|
+
val = get(mid)
|
2106
|
+
v = yield val
|
2107
|
+
if v.is_a? Numeric
|
2108
|
+
if v == 0
|
2109
|
+
return val
|
2110
|
+
elsif v > 0
|
2111
|
+
high = mid
|
2112
|
+
else
|
2113
|
+
low = mid + 1
|
2114
|
+
end
|
2115
|
+
elsif v == true
|
2116
|
+
result = val
|
2117
|
+
high = mid
|
2118
|
+
elsif !v
|
2119
|
+
low = mid + 1
|
2120
|
+
else
|
2121
|
+
raise TypeError, "wrong argument type #{v.class} (must be numeric, true, false, or nil)"
|
2122
|
+
end
|
2123
|
+
end
|
2124
|
+
result
|
2125
|
+
end
|
2126
|
+
|
2127
|
+
# Return an empty `Vector` instance, of the same class as this one. Useful if you
|
2128
|
+
# have multiple subclasses of `Vector` and want to treat them polymorphically.
|
2129
|
+
#
|
2130
|
+
# @return [Vector]
|
2131
|
+
def clear
|
2132
|
+
self.class.empty
|
2133
|
+
end
|
2134
|
+
|
2135
|
+
# Return a randomly chosen item from this `Vector`. If the vector is empty, return `nil`.
|
2136
|
+
#
|
2137
|
+
# @example
|
2138
|
+
# Immutable::Vector[1, 2, 3, 4, 5].sample # => 2
|
2139
|
+
#
|
2140
|
+
# @return [Object]
|
2141
|
+
def sample
|
2142
|
+
get(rand(@size))
|
2143
|
+
end
|
2144
|
+
|
2145
|
+
# Return a new `Vector` with only the elements at the given `indices`, in the
|
2146
|
+
# order specified by `indices`. If any of the `indices` do not exist, `nil`s will
|
2147
|
+
# appear in their places.
|
2148
|
+
#
|
2149
|
+
# @example
|
2150
|
+
# v = Immutable::Vector["A", "B", "C", "D", "E", "F"]
|
2151
|
+
# v.values_at(2, 4, 5) # => Immutable::Vector["C", "E", "F"]
|
2152
|
+
#
|
2153
|
+
# @param indices [Array] The indices to retrieve and gather into a new `Vector`
|
2154
|
+
# @return [Vector]
|
2155
|
+
def values_at(*indices)
|
2156
|
+
self.class.new(indices.map { |i| get(i) }.freeze)
|
2157
|
+
end
|
2158
|
+
|
2159
|
+
# Find the index of an element, starting from the end of the vector.
|
2160
|
+
# Returns `nil` if no element is found.
|
2161
|
+
#
|
2162
|
+
# @overload rindex(obj)
|
2163
|
+
# Return the index of the last element which is `#==` to `obj`.
|
2164
|
+
#
|
2165
|
+
# @example
|
2166
|
+
# v = Immutable::Vector[7, 8, 9, 7, 8, 9]
|
2167
|
+
# v.rindex(8) # => 4
|
2168
|
+
#
|
2169
|
+
# @overload rindex
|
2170
|
+
# Return the index of the last element for which the block returns true.
|
2171
|
+
#
|
2172
|
+
# @yield [element] Once for each element, last to first, until the block
|
2173
|
+
# returns true.
|
2174
|
+
# @example
|
2175
|
+
# v = Immutable::Vector[7, 8, 9, 7, 8, 9]
|
2176
|
+
# v.rindex { |e| e.even? } # => 4
|
2177
|
+
#
|
2178
|
+
# @return [Integer]
|
2179
|
+
def rindex(obj = (missing_arg = true))
|
2180
|
+
i = @size - 1
|
2181
|
+
if missing_arg
|
2182
|
+
if block_given?
|
2183
|
+
reverse_each { |item| return i if yield item; i -= 1 }
|
2184
|
+
nil
|
2185
|
+
else
|
2186
|
+
enum_for(:rindex)
|
2187
|
+
end
|
2188
|
+
else
|
2189
|
+
reverse_each { |item| return i if item == obj; i -= 1 }
|
2190
|
+
nil
|
2191
|
+
end
|
2192
|
+
end
|
2193
|
+
|
2194
|
+
# Assumes all elements are nested, indexable collections, and searches through them,
|
2195
|
+
# comparing `obj` with the first element of each nested collection. Return the
|
2196
|
+
# first nested collection which matches, or `nil` if none is found.
|
2197
|
+
# Behaviour is undefined when elements do not meet assumptions (i.e. are
|
2198
|
+
# not indexable collections).
|
2199
|
+
#
|
2200
|
+
# @example
|
2201
|
+
# v = Immutable::Vector[["A", 10], ["B", 20], ["C", 30]]
|
2202
|
+
# v.assoc("B") # => ["B", 20]
|
2203
|
+
#
|
2204
|
+
# @param obj [Object] The object to search for
|
2205
|
+
# @return [Object]
|
2206
|
+
def assoc(obj)
|
2207
|
+
each do |array|
|
2208
|
+
next if !array.respond_to?(:[])
|
2209
|
+
return array if obj == array[0]
|
2210
|
+
end
|
2211
|
+
nil
|
2212
|
+
end
|
2213
|
+
|
2214
|
+
# Assumes all elements are nested, indexable collections, and searches through them,
|
2215
|
+
# comparing `obj` with the second element of each nested collection. Return
|
2216
|
+
# the first nested collection which matches, or `nil` if none is found.
|
2217
|
+
# Behaviour is undefined when elements do not meet assumptions (i.e. are
|
2218
|
+
# not indexable collections).
|
2219
|
+
#
|
2220
|
+
# @example
|
2221
|
+
# v = Immutable::Vector[["A", 10], ["B", 20], ["C", 30]]
|
2222
|
+
# v.rassoc(20) # => ["B", 20]
|
2223
|
+
#
|
2224
|
+
# @param obj [Object] The object to search for
|
2225
|
+
# @return [Object]
|
2226
|
+
def rassoc(obj)
|
2227
|
+
each do |array|
|
2228
|
+
next if !array.respond_to?(:[])
|
2229
|
+
return array if obj == array[1]
|
2230
|
+
end
|
2231
|
+
nil
|
2232
|
+
end
|
2233
|
+
|
2234
|
+
# Return an `Array` with the same elements, in the same order. The returned
|
2235
|
+
# `Array` may or may not be frozen.
|
2236
|
+
#
|
2237
|
+
# @return [Array]
|
2238
|
+
def to_a
|
2239
|
+
if @levels == 0
|
2240
|
+
# When initializing a Vector with 32 or less items, we always make
|
2241
|
+
# sure @root is frozen, so we can return it directly here
|
2242
|
+
@root
|
2243
|
+
else
|
2244
|
+
flatten_node(@root, @levels * BITS_PER_LEVEL, [])
|
2245
|
+
end
|
2246
|
+
end
|
2247
|
+
alias to_ary to_a
|
2248
|
+
|
2249
|
+
# Return true if `other` has the same type and contents as this `Vector`.
|
2250
|
+
#
|
2251
|
+
# @param other [Object] The collection to compare with
|
2252
|
+
# @return [Boolean]
|
2253
|
+
def eql?(other)
|
2254
|
+
return true if other.equal?(self)
|
2255
|
+
return false unless instance_of?(other.class) && @size == other.size
|
2256
|
+
@root.eql?(other.instance_variable_get(:@root))
|
2257
|
+
end
|
2258
|
+
|
2259
|
+
# See `Object#hash`.
|
2260
|
+
# @return [Integer]
|
2261
|
+
def hash
|
2262
|
+
reduce(0) { |hash, item| (hash << 5) - hash + item.hash }
|
2263
|
+
end
|
2264
|
+
|
2265
|
+
# Return `self`. Since this is an immutable object duplicates are
|
2266
|
+
# equivalent.
|
2267
|
+
# @return [Vector]
|
2268
|
+
def dup
|
2269
|
+
self
|
2270
|
+
end
|
2271
|
+
alias clone dup
|
2272
|
+
|
2273
|
+
# @return [::Array]
|
2274
|
+
# @private
|
2275
|
+
def marshal_dump
|
2276
|
+
to_a
|
2277
|
+
end
|
2278
|
+
|
2279
|
+
# @private
|
2280
|
+
def marshal_load(array)
|
2281
|
+
initialize(array.freeze)
|
2282
|
+
end
|
2283
|
+
|
2284
|
+
private
|
2285
|
+
|
2286
|
+
def traverse_depth_first(node, level, &block)
|
2287
|
+
return node.each(&block) if level == 0
|
2288
|
+
node.each { |child| traverse_depth_first(child, level - 1, &block) }
|
2289
|
+
end
|
2290
|
+
|
2291
|
+
def reverse_traverse_depth_first(node, level, &block)
|
2292
|
+
return node.reverse_each(&block) if level == 0
|
2293
|
+
node.reverse_each { |child| reverse_traverse_depth_first(child, level - 1, &block) }
|
2294
|
+
end
|
2295
|
+
|
2296
|
+
def leaf_node_for(node, bitshift, index)
|
2297
|
+
while bitshift > 0
|
2298
|
+
node = node[(index >> bitshift) & INDEX_MASK]
|
2299
|
+
bitshift -= BITS_PER_LEVEL
|
2300
|
+
end
|
2301
|
+
node
|
2302
|
+
end
|
2303
|
+
|
2304
|
+
def update_root(index, item)
|
2305
|
+
root, levels = @root, @levels
|
2306
|
+
while index >= (1 << (BITS_PER_LEVEL * (levels + 1)))
|
2307
|
+
root = [root].freeze
|
2308
|
+
levels += 1
|
2309
|
+
end
|
2310
|
+
new_root = update_leaf_node(root, levels * BITS_PER_LEVEL, index, item)
|
2311
|
+
if new_root.equal?(root)
|
2312
|
+
self
|
2313
|
+
else
|
2314
|
+
self.class.alloc(new_root, @size > index ? @size : index + 1, levels)
|
2315
|
+
end
|
2316
|
+
end
|
2317
|
+
|
2318
|
+
def update_leaf_node(node, bitshift, index, item)
|
2319
|
+
slot_index = (index >> bitshift) & INDEX_MASK
|
2320
|
+
if bitshift > 0
|
2321
|
+
old_child = node[slot_index] || []
|
2322
|
+
item = update_leaf_node(old_child, bitshift - BITS_PER_LEVEL, index, item)
|
2323
|
+
end
|
2324
|
+
existing_item = node[slot_index]
|
2325
|
+
if existing_item.equal?(item)
|
2326
|
+
node
|
2327
|
+
else
|
2328
|
+
node.dup.tap { |n| n[slot_index] = item }.freeze
|
2329
|
+
end
|
2330
|
+
end
|
2331
|
+
|
2332
|
+
def flatten_range(node, bitshift, from, to)
|
2333
|
+
from_slot = (from >> bitshift) & INDEX_MASK
|
2334
|
+
to_slot = (to >> bitshift) & INDEX_MASK
|
2335
|
+
|
2336
|
+
if bitshift == 0 # are we at the bottom?
|
2337
|
+
node.slice(from_slot, to_slot-from_slot+1)
|
2338
|
+
elsif from_slot == to_slot
|
2339
|
+
flatten_range(node[from_slot], bitshift - BITS_PER_LEVEL, from, to)
|
2340
|
+
else
|
2341
|
+
# the following bitmask can be used to pick out the part of the from/to indices
|
2342
|
+
# which will be used to direct path BELOW this node
|
2343
|
+
mask = ((1 << bitshift) - 1)
|
2344
|
+
result = []
|
2345
|
+
|
2346
|
+
if from & mask == 0
|
2347
|
+
flatten_node(node[from_slot], bitshift - BITS_PER_LEVEL, result)
|
2348
|
+
else
|
2349
|
+
result.concat(flatten_range(node[from_slot], bitshift - BITS_PER_LEVEL, from, from | mask))
|
2350
|
+
end
|
2351
|
+
|
2352
|
+
(from_slot+1).upto(to_slot-1) do |slot_index|
|
2353
|
+
flatten_node(node[slot_index], bitshift - BITS_PER_LEVEL, result)
|
2354
|
+
end
|
2355
|
+
|
2356
|
+
if to & mask == mask
|
2357
|
+
flatten_node(node[to_slot], bitshift - BITS_PER_LEVEL, result)
|
2358
|
+
else
|
2359
|
+
result.concat(flatten_range(node[to_slot], bitshift - BITS_PER_LEVEL, to & ~mask, to))
|
2360
|
+
end
|
2361
|
+
|
2362
|
+
result
|
2363
|
+
end
|
2364
|
+
end
|
2365
|
+
|
2366
|
+
def flatten_node(node, bitshift, result)
|
2367
|
+
if bitshift == 0
|
2368
|
+
result.concat(node)
|
2369
|
+
elsif bitshift == BITS_PER_LEVEL
|
2370
|
+
node.each { |a| result.concat(a) }
|
2371
|
+
else
|
2372
|
+
bitshift -= BITS_PER_LEVEL
|
2373
|
+
node.each { |a| flatten_node(a, bitshift, result) }
|
2374
|
+
end
|
2375
|
+
result
|
2376
|
+
end
|
2377
|
+
|
2378
|
+
def subsequence(from, length)
|
2379
|
+
return nil if from > @size || from < 0 || length < 0
|
2380
|
+
length = @size - from if @size < from + length
|
2381
|
+
return self.class.empty if length == 0
|
2382
|
+
self.class.new(flatten_range(@root, @levels * BITS_PER_LEVEL, from, from + length - 1))
|
2383
|
+
end
|
2384
|
+
|
2385
|
+
def flatten_suffix(node, bitshift, from, result)
|
2386
|
+
from_slot = (from >> bitshift) & INDEX_MASK
|
2387
|
+
|
2388
|
+
if bitshift == 0
|
2389
|
+
if from_slot == 0
|
2390
|
+
result.concat(node)
|
2391
|
+
else
|
2392
|
+
result.concat(node.slice(from_slot, 32)) # entire suffix of node. excess length is ignored by #slice
|
2393
|
+
end
|
2394
|
+
else
|
2395
|
+
mask = ((1 << bitshift) - 1)
|
2396
|
+
if from & mask == 0
|
2397
|
+
from_slot.upto(node.size-1) do |i|
|
2398
|
+
flatten_node(node[i], bitshift - BITS_PER_LEVEL, result)
|
2399
|
+
end
|
2400
|
+
elsif (child = node[from_slot])
|
2401
|
+
flatten_suffix(child, bitshift - BITS_PER_LEVEL, from, result)
|
2402
|
+
(from_slot+1).upto(node.size-1) do |i|
|
2403
|
+
flatten_node(node[i], bitshift - BITS_PER_LEVEL, result)
|
2404
|
+
end
|
2405
|
+
end
|
2406
|
+
result
|
2407
|
+
end
|
2408
|
+
end
|
2409
|
+
|
2410
|
+
def replace_suffix(from, suffix)
|
2411
|
+
# new suffix can go directly after existing elements
|
2412
|
+
raise IndexError if from > @size
|
2413
|
+
root, levels = @root, @levels
|
2414
|
+
|
2415
|
+
if (from >> (BITS_PER_LEVEL * (@levels + 1))) != 0
|
2416
|
+
# index where new suffix goes doesn't fall within current tree
|
2417
|
+
# we will need to deepen tree
|
2418
|
+
root = [root].freeze
|
2419
|
+
levels += 1
|
2420
|
+
end
|
2421
|
+
|
2422
|
+
new_size = from + suffix.size
|
2423
|
+
root = replace_node_suffix(root, levels * BITS_PER_LEVEL, from, suffix)
|
2424
|
+
|
2425
|
+
if !suffix.empty?
|
2426
|
+
levels.times { suffix = suffix.each_slice(32).to_a }
|
2427
|
+
root.concat(suffix)
|
2428
|
+
while root.size > 32
|
2429
|
+
root = root.each_slice(32).to_a
|
2430
|
+
levels += 1
|
2431
|
+
end
|
2432
|
+
else
|
2433
|
+
while root.size == 1 && levels > 0
|
2434
|
+
root = root[0]
|
2435
|
+
levels -= 1
|
2436
|
+
end
|
2437
|
+
end
|
2438
|
+
|
2439
|
+
self.class.alloc(root.freeze, new_size, levels)
|
2440
|
+
end
|
2441
|
+
|
2442
|
+
def replace_node_suffix(node, bitshift, from, suffix)
|
2443
|
+
from_slot = (from >> bitshift) & INDEX_MASK
|
2444
|
+
|
2445
|
+
if bitshift == 0
|
2446
|
+
if from_slot == 0
|
2447
|
+
suffix.shift(32)
|
2448
|
+
else
|
2449
|
+
node.take(from_slot).concat(suffix.shift(32 - from_slot))
|
2450
|
+
end
|
2451
|
+
else
|
2452
|
+
mask = ((1 << bitshift) - 1)
|
2453
|
+
if from & mask == 0
|
2454
|
+
if from_slot == 0
|
2455
|
+
new_node = suffix.shift(32 * (1 << bitshift))
|
2456
|
+
while bitshift != 0
|
2457
|
+
new_node = new_node.each_slice(32).to_a
|
2458
|
+
bitshift -= BITS_PER_LEVEL
|
2459
|
+
end
|
2460
|
+
new_node
|
2461
|
+
else
|
2462
|
+
result = node.take(from_slot)
|
2463
|
+
remainder = suffix.shift((32 - from_slot) * (1 << bitshift))
|
2464
|
+
while bitshift != 0
|
2465
|
+
remainder = remainder.each_slice(32).to_a
|
2466
|
+
bitshift -= BITS_PER_LEVEL
|
2467
|
+
end
|
2468
|
+
result.concat(remainder)
|
2469
|
+
end
|
2470
|
+
elsif (child = node[from_slot])
|
2471
|
+
result = node.take(from_slot)
|
2472
|
+
result.push(replace_node_suffix(child, bitshift - BITS_PER_LEVEL, from, suffix))
|
2473
|
+
remainder = suffix.shift((31 - from_slot) * (1 << bitshift))
|
2474
|
+
while bitshift != 0
|
2475
|
+
remainder = remainder.each_slice(32).to_a
|
2476
|
+
bitshift -= BITS_PER_LEVEL
|
2477
|
+
end
|
2478
|
+
result.concat(remainder)
|
2479
|
+
else
|
2480
|
+
raise "Shouldn't happen"
|
2481
|
+
end
|
2482
|
+
end
|
2483
|
+
end
|
2484
|
+
end
|
2485
|
+
|
2486
|
+
# The canonical empty `Vector`. Returned by `Vector[]` when
|
2487
|
+
# invoked with no arguments; also returned by `Vector.empty`. Prefer using this
|
2488
|
+
# one rather than creating many empty vectors using `Vector.new`.
|
2489
|
+
#
|
2490
|
+
# @private
|
2491
|
+
EmptyVector = Immutable::Vector.empty
|
2492
|
+
|
2493
|
+
|
2494
|
+
# `Immutable::Set` is a collection of unordered values with no duplicates. Testing whether
|
2495
|
+
# an object is present in the `Set` can be done in constant time. `Set` is also `Enumerable`, so you can
|
2496
|
+
# iterate over the members of the set with {#each}, transform them with {#map}, filter
|
2497
|
+
# them with {#select}, and so on. Some of the `Enumerable` methods are overridden to
|
2498
|
+
# return `immutable-ruby` collections.
|
2499
|
+
#
|
2500
|
+
# Like the `Set` class in Ruby's standard library, which we will call RubySet,
|
2501
|
+
# `Immutable::Set` defines equivalency of objects using `#hash` and `#eql?`. No two
|
2502
|
+
# objects with the same `#hash` code, and which are also `#eql?`, can coexist in the
|
2503
|
+
# same `Set`. If one is already in the `Set`, attempts to add another one will have
|
2504
|
+
# no effect.
|
2505
|
+
#
|
2506
|
+
# `Set`s have no natural ordering and cannot be compared using `#<=>`. However, they
|
2507
|
+
# define {#<}, {#>}, {#<=}, and {#>=} as shorthand for {#proper_subset?},
|
2508
|
+
# {#proper_superset?}, {#subset?}, and {#superset?} respectively.
|
2509
|
+
#
|
2510
|
+
# The basic set-theoretic operations {#union}, {#intersection}, {#difference}, and
|
2511
|
+
# {#exclusion} work with any `Enumerable` object.
|
2512
|
+
#
|
2513
|
+
# A `Set` can be created in either of the following ways:
|
2514
|
+
#
|
2515
|
+
# Immutable::Set.new([1, 2, 3]) # any Enumerable can be used to initialize
|
2516
|
+
# Immutable::Set['A', 'B', 'C', 'D']
|
2517
|
+
#
|
2518
|
+
# The latter 2 forms of initialization can be used with your own, custom subclasses
|
2519
|
+
# of `Immutable::Set`.
|
2520
|
+
#
|
2521
|
+
# Unlike RubySet, all methods which you might expect to "modify" an `Immutable::Set`
|
2522
|
+
# actually return a new set and leave the existing one unchanged.
|
2523
|
+
#
|
2524
|
+
# @example
|
2525
|
+
# set1 = Immutable::Set[1, 2] # => Immutable::Set[1, 2]
|
2526
|
+
# set2 = Immutable::Set[1, 2] # => Immutable::Set[1, 2]
|
2527
|
+
# set1 == set2 # => true
|
2528
|
+
# set3 = set1.add("foo") # => Immutable::Set[1, 2, "foo"]
|
2529
|
+
# set3 - set2 # => Immutable::Set["foo"]
|
2530
|
+
# set3.subset?(set1) # => false
|
2531
|
+
# set1.subset?(set3) # => true
|
2532
|
+
#
|
2533
|
+
class Set
|
2534
|
+
include Immutable::Enumerable
|
2535
|
+
|
2536
|
+
class << self
|
2537
|
+
# Create a new `Set` populated with the given items.
|
2538
|
+
# @return [Set]
|
2539
|
+
def [](*items)
|
2540
|
+
items.empty? ? empty : new(items)
|
2541
|
+
end
|
2542
|
+
|
2543
|
+
# Return an empty `Set`. If used on a subclass, returns an empty instance
|
2544
|
+
# of that class.
|
2545
|
+
#
|
2546
|
+
# @return [Set]
|
2547
|
+
def empty
|
2548
|
+
@empty ||= new
|
2549
|
+
end
|
2550
|
+
|
2551
|
+
# "Raw" allocation of a new `Set`. Used internally to create a new
|
2552
|
+
# instance quickly after obtaining a modified {Trie}.
|
2553
|
+
#
|
2554
|
+
# @return [Set]
|
2555
|
+
# @private
|
2556
|
+
def alloc(trie = EmptyTrie)
|
2557
|
+
allocate.tap { |s| s.instance_variable_set(:@trie, trie) }.freeze
|
2558
|
+
end
|
2559
|
+
end
|
2560
|
+
|
2561
|
+
def initialize(items=[])
|
2562
|
+
@trie = Trie.new(0)
|
2563
|
+
items.each { |item| @trie.put!(item, nil) }
|
2564
|
+
freeze
|
2565
|
+
end
|
2566
|
+
|
2567
|
+
# Return `true` if this `Set` contains no items.
|
2568
|
+
# @return [Boolean]
|
2569
|
+
def empty?
|
2570
|
+
@trie.empty?
|
2571
|
+
end
|
2572
|
+
|
2573
|
+
# Return the number of items in this `Set`.
|
2574
|
+
# @return [Integer]
|
2575
|
+
def size
|
2576
|
+
@trie.size
|
2577
|
+
end
|
2578
|
+
alias length size
|
2579
|
+
|
2580
|
+
# Return a new `Set` with `item` added. If `item` is already in the set,
|
2581
|
+
# return `self`.
|
2582
|
+
#
|
2583
|
+
# @example
|
2584
|
+
# Immutable::Set[1, 2, 3].add(4) # => Immutable::Set[1, 2, 4, 3]
|
2585
|
+
# Immutable::Set[1, 2, 3].add(2) # => Immutable::Set[1, 2, 3]
|
2586
|
+
#
|
2587
|
+
# @param item [Object] The object to add
|
2588
|
+
# @return [Set]
|
2589
|
+
def add(item)
|
2590
|
+
include?(item) ? self : self.class.alloc(@trie.put(item, nil))
|
2591
|
+
end
|
2592
|
+
alias << add
|
2593
|
+
|
2594
|
+
# If `item` is not a member of this `Set`, return a new `Set` with `item` added.
|
2595
|
+
# Otherwise, return `false`.
|
2596
|
+
#
|
2597
|
+
# @example
|
2598
|
+
# Immutable::Set[1, 2, 3].add?(4) # => Immutable::Set[1, 2, 4, 3]
|
2599
|
+
# Immutable::Set[1, 2, 3].add?(2) # => false
|
2600
|
+
#
|
2601
|
+
# @param item [Object] The object to add
|
2602
|
+
# @return [Set, false]
|
2603
|
+
def add?(item)
|
2604
|
+
!include?(item) && add(item)
|
2605
|
+
end
|
2606
|
+
|
2607
|
+
# Return a new `Set` with `item` removed. If `item` is not a member of the set,
|
2608
|
+
# return `self`.
|
2609
|
+
#
|
2610
|
+
# @example
|
2611
|
+
# Immutable::Set[1, 2, 3].delete(1) # => Immutable::Set[2, 3]
|
2612
|
+
# Immutable::Set[1, 2, 3].delete(99) # => Immutable::Set[1, 2, 3]
|
2613
|
+
#
|
2614
|
+
# @param item [Object] The object to remove
|
2615
|
+
# @return [Set]
|
2616
|
+
def delete(item)
|
2617
|
+
trie = @trie.delete(item)
|
2618
|
+
new_trie(trie)
|
2619
|
+
end
|
2620
|
+
|
2621
|
+
# If `item` is a member of this `Set`, return a new `Set` with `item` removed.
|
2622
|
+
# Otherwise, return `false`.
|
2623
|
+
#
|
2624
|
+
# @example
|
2625
|
+
# Immutable::Set[1, 2, 3].delete?(1) # => Immutable::Set[2, 3]
|
2626
|
+
# Immutable::Set[1, 2, 3].delete?(99) # => false
|
2627
|
+
#
|
2628
|
+
# @param item [Object] The object to remove
|
2629
|
+
# @return [Set, false]
|
2630
|
+
def delete?(item)
|
2631
|
+
include?(item) && delete(item)
|
2632
|
+
end
|
2633
|
+
|
2634
|
+
# Call the block once for each item in this `Set`. No specific iteration order
|
2635
|
+
# is guaranteed, but the order will be stable for any particular `Set`. If
|
2636
|
+
# no block is given, an `Enumerator` is returned instead.
|
2637
|
+
#
|
2638
|
+
# @example
|
2639
|
+
# Immutable::Set["Dog", "Elephant", "Lion"].each { |e| puts e }
|
2640
|
+
# Elephant
|
2641
|
+
# Dog
|
2642
|
+
# Lion
|
2643
|
+
# # => Immutable::Set["Dog", "Elephant", "Lion"]
|
2644
|
+
#
|
2645
|
+
# @yield [item] Once for each item.
|
2646
|
+
# @return [self, Enumerator]
|
2647
|
+
def each
|
2648
|
+
return to_enum if not block_given?
|
2649
|
+
@trie.each { |key, _| yield(key) }
|
2650
|
+
self
|
2651
|
+
end
|
2652
|
+
|
2653
|
+
# Call the block once for each item in this `Set`. Iteration order will be
|
2654
|
+
# the opposite of {#each}. If no block is given, an `Enumerator` is
|
2655
|
+
# returned instead.
|
2656
|
+
#
|
2657
|
+
# @example
|
2658
|
+
# Immutable::Set["Dog", "Elephant", "Lion"].reverse_each { |e| puts e }
|
2659
|
+
# Lion
|
2660
|
+
# Dog
|
2661
|
+
# Elephant
|
2662
|
+
# # => Immutable::Set["Dog", "Elephant", "Lion"]
|
2663
|
+
#
|
2664
|
+
# @yield [item] Once for each item.
|
2665
|
+
# @return [self]
|
2666
|
+
def reverse_each
|
2667
|
+
return enum_for(:reverse_each) if not block_given?
|
2668
|
+
@trie.reverse_each { |key, _| yield(key) }
|
2669
|
+
self
|
2670
|
+
end
|
2671
|
+
|
2672
|
+
# Return a new `Set` with all the items for which the block returns true.
|
2673
|
+
#
|
2674
|
+
# @example
|
2675
|
+
# Immutable::Set["Elephant", "Dog", "Lion"].select { |e| e.size >= 4 }
|
2676
|
+
# # => Immutable::Set["Elephant", "Lion"]
|
2677
|
+
# @yield [item] Once for each item.
|
2678
|
+
# @return [Set]
|
2679
|
+
def select
|
2680
|
+
return enum_for(:select) unless block_given?
|
2681
|
+
trie = @trie.select { |key, _| yield(key) }
|
2682
|
+
new_trie(trie)
|
2683
|
+
end
|
2684
|
+
alias find_all select
|
2685
|
+
alias keep_if select
|
2686
|
+
|
2687
|
+
# Call the block once for each item in this `Set`. All the values returned
|
2688
|
+
# from the block will be gathered into a new `Set`. If no block is given,
|
2689
|
+
# an `Enumerator` is returned instead.
|
2690
|
+
#
|
2691
|
+
# @example
|
2692
|
+
# Immutable::Set["Cat", "Elephant", "Dog", "Lion"].map { |e| e.size }
|
2693
|
+
# # => Immutable::Set[8, 4, 3]
|
2694
|
+
#
|
2695
|
+
# @yield [item] Once for each item.
|
2696
|
+
# @return [Set]
|
2697
|
+
def map
|
2698
|
+
return enum_for(:map) if not block_given?
|
2699
|
+
return self if empty?
|
2700
|
+
self.class.new(super)
|
2701
|
+
end
|
2702
|
+
alias collect map
|
2703
|
+
|
2704
|
+
# Return `true` if the given item is present in this `Set`. More precisely,
|
2705
|
+
# return `true` if an object with the same `#hash` code, and which is also `#eql?`
|
2706
|
+
# to the given object is present.
|
2707
|
+
#
|
2708
|
+
# @example
|
2709
|
+
# Immutable::Set["A", "B", "C"].include?("B") # => true
|
2710
|
+
# Immutable::Set["A", "B", "C"].include?("Z") # => false
|
2711
|
+
#
|
2712
|
+
# @param object [Object] The object to check for
|
2713
|
+
# @return [Boolean]
|
2714
|
+
def include?(object)
|
2715
|
+
@trie.key?(object)
|
2716
|
+
end
|
2717
|
+
alias member? include?
|
2718
|
+
|
2719
|
+
# Return a member of this `Set`. The member chosen will be the first one which
|
2720
|
+
# would be yielded by {#each}. If the set is empty, return `nil`.
|
2721
|
+
#
|
2722
|
+
# @example
|
2723
|
+
# Immutable::Set["A", "B", "C"].first # => "C"
|
2724
|
+
#
|
2725
|
+
# @return [Object]
|
2726
|
+
def first
|
2727
|
+
(entry = @trie.at(0)) && entry[0]
|
2728
|
+
end
|
2729
|
+
|
2730
|
+
# Return a {SortedSet} which contains the same items as this `Set`, ordered by
|
2731
|
+
# the given comparator block.
|
2732
|
+
#
|
2733
|
+
# @example
|
2734
|
+
# Immutable::Set["Elephant", "Dog", "Lion"].sort
|
2735
|
+
# # => Immutable::SortedSet["Dog", "Elephant", "Lion"]
|
2736
|
+
# Immutable::Set["Elephant", "Dog", "Lion"].sort { |a,b| a.size <=> b.size }
|
2737
|
+
# # => Immutable::SortedSet["Dog", "Lion", "Elephant"]
|
2738
|
+
#
|
2739
|
+
# @yield [a, b] Any number of times with different pairs of elements.
|
2740
|
+
# @yieldreturn [Integer] Negative if the first element should be sorted
|
2741
|
+
# lower, positive if the latter element, or 0 if
|
2742
|
+
# equal.
|
2743
|
+
# @return [SortedSet]
|
2744
|
+
def sort(&comparator)
|
2745
|
+
SortedSet.new(to_a, &comparator)
|
2746
|
+
end
|
2747
|
+
|
2748
|
+
# Return a {SortedSet} which contains the same items as this `Set`, ordered
|
2749
|
+
# by mapping each item through the provided block to obtain sort keys, and
|
2750
|
+
# then sorting the keys.
|
2751
|
+
#
|
2752
|
+
# @example
|
2753
|
+
# Immutable::Set["Elephant", "Dog", "Lion"].sort_by { |e| e.size }
|
2754
|
+
# # => Immutable::SortedSet["Dog", "Lion", "Elephant"]
|
2755
|
+
#
|
2756
|
+
# @yield [item] Once for each item to create the set, and then potentially
|
2757
|
+
# again depending on what operations are performed on the
|
2758
|
+
# returned {SortedSet}. As such, it is recommended that the
|
2759
|
+
# block be a pure function.
|
2760
|
+
# @yieldreturn [Object] sort key for the item
|
2761
|
+
# @return [SortedSet]
|
2762
|
+
def sort_by(&mapper)
|
2763
|
+
SortedSet.new(to_a, &mapper)
|
2764
|
+
end
|
2765
|
+
|
2766
|
+
# Return a new `Set` which contains all the members of both this `Set` and `other`.
|
2767
|
+
# `other` can be any `Enumerable` object.
|
2768
|
+
#
|
2769
|
+
# @example
|
2770
|
+
# Immutable::Set[1, 2] | Immutable::Set[2, 3] # => Immutable::Set[1, 2, 3]
|
2771
|
+
#
|
2772
|
+
# @param other [Enumerable] The collection to merge with
|
2773
|
+
# @return [Set]
|
2774
|
+
def union(other)
|
2775
|
+
if other.is_a?(Immutable::Set)
|
2776
|
+
if other.size > size
|
2777
|
+
small_set_pairs = @trie
|
2778
|
+
large_set_trie = other.instance_variable_get(:@trie)
|
2779
|
+
else
|
2780
|
+
small_set_pairs = other.instance_variable_get(:@trie)
|
2781
|
+
large_set_trie = @trie
|
2782
|
+
end
|
2783
|
+
else
|
2784
|
+
if other.respond_to?(:lazy)
|
2785
|
+
small_set_pairs = other.lazy.map { |e| [e, nil] }
|
2786
|
+
else
|
2787
|
+
small_set_pairs = other.map { |e| [e, nil] }
|
2788
|
+
end
|
2789
|
+
large_set_trie = @trie
|
2790
|
+
end
|
2791
|
+
|
2792
|
+
trie = large_set_trie.bulk_put(small_set_pairs)
|
2793
|
+
new_trie(trie)
|
2794
|
+
end
|
2795
|
+
alias | union
|
2796
|
+
alias + union
|
2797
|
+
alias merge union
|
2798
|
+
|
2799
|
+
# Return a new `Set` which contains all the items which are members of both
|
2800
|
+
# this `Set` and `other`. `other` can be any `Enumerable` object.
|
2801
|
+
#
|
2802
|
+
# @example
|
2803
|
+
# Immutable::Set[1, 2] & Immutable::Set[2, 3] # => Immutable::Set[2]
|
2804
|
+
#
|
2805
|
+
# @param other [Enumerable] The collection to intersect with
|
2806
|
+
# @return [Set]
|
2807
|
+
def intersection(other)
|
2808
|
+
if other.size < @trie.size
|
2809
|
+
if other.is_a?(Immutable::Set)
|
2810
|
+
trie = other.instance_variable_get(:@trie).select { |key, _| include?(key) }
|
2811
|
+
else
|
2812
|
+
trie = Trie.new(0)
|
2813
|
+
other.each { |obj| trie.put!(obj, nil) if include?(obj) }
|
2814
|
+
end
|
2815
|
+
else
|
2816
|
+
trie = @trie.select { |key, _| other.include?(key) }
|
2817
|
+
end
|
2818
|
+
new_trie(trie)
|
2819
|
+
end
|
2820
|
+
alias & intersection
|
2821
|
+
|
2822
|
+
# Return a new `Set` with all the items in `other` removed. `other` can be
|
2823
|
+
# any `Enumerable` object.
|
2824
|
+
#
|
2825
|
+
# @example
|
2826
|
+
# Immutable::Set[1, 2] - Immutable::Set[2, 3] # => Immutable::Set[1]
|
2827
|
+
#
|
2828
|
+
# @param other [Enumerable] The collection to subtract from this set
|
2829
|
+
# @return [Set]
|
2830
|
+
def difference(other)
|
2831
|
+
trie = if (@trie.size <= other.size) && (other.is_a?(Immutable::Set) || (defined?(::Set) && other.is_a?(::Set)))
|
2832
|
+
@trie.select { |key, _| !other.include?(key) }
|
2833
|
+
else
|
2834
|
+
@trie.bulk_delete(other)
|
2835
|
+
end
|
2836
|
+
new_trie(trie)
|
2837
|
+
end
|
2838
|
+
alias subtract difference
|
2839
|
+
alias - difference
|
2840
|
+
|
2841
|
+
# Return a new `Set` which contains all the items which are members of this
|
2842
|
+
# `Set` or of `other`, but not both. `other` can be any `Enumerable` object.
|
2843
|
+
#
|
2844
|
+
# @example
|
2845
|
+
# Immutable::Set[1, 2] ^ Immutable::Set[2, 3] # => Immutable::Set[1, 3]
|
2846
|
+
#
|
2847
|
+
# @param other [Enumerable] The collection to take the exclusive disjunction of
|
2848
|
+
# @return [Set]
|
2849
|
+
def exclusion(other)
|
2850
|
+
((self | other) - (self & other))
|
2851
|
+
end
|
2852
|
+
alias ^ exclusion
|
2853
|
+
|
2854
|
+
# Return `true` if all items in this `Set` are also in `other`.
|
2855
|
+
#
|
2856
|
+
# @example
|
2857
|
+
# Immutable::Set[2, 3].subset?(Immutable::Set[1, 2, 3]) # => true
|
2858
|
+
#
|
2859
|
+
# @param other [Set]
|
2860
|
+
# @return [Boolean]
|
2861
|
+
def subset?(other)
|
2862
|
+
return false if other.size < size
|
2863
|
+
|
2864
|
+
# This method has the potential to be very slow if 'other' is a large Array, so to avoid that,
|
2865
|
+
# we convert those Arrays to Sets before checking presence of items
|
2866
|
+
# Time to convert Array -> Set is linear in array.size
|
2867
|
+
# Time to check for presence of all items in an Array is proportional to set.size * array.size
|
2868
|
+
# Note that both sides of that equation have array.size -- hence those terms cancel out,
|
2869
|
+
# and the break-even point is solely dependent on the size of this collection
|
2870
|
+
# After doing some benchmarking to estimate the constants, it appears break-even is at ~190 items
|
2871
|
+
# We also check other.size, to avoid the more expensive #is_a? checks in cases where it doesn't matter
|
2872
|
+
#
|
2873
|
+
if other.size >= 150 && @trie.size >= 190 && !(other.is_a?(Immutable::Set) || other.is_a?(::Set))
|
2874
|
+
other = ::Set.new(other)
|
2875
|
+
end
|
2876
|
+
all? { |item| other.include?(item) }
|
2877
|
+
end
|
2878
|
+
alias <= subset?
|
2879
|
+
|
2880
|
+
# Return `true` if all items in `other` are also in this `Set`.
|
2881
|
+
#
|
2882
|
+
# @example
|
2883
|
+
# Immutable::Set[1, 2, 3].superset?(Immutable::Set[2, 3]) # => true
|
2884
|
+
#
|
2885
|
+
# @param other [Set]
|
2886
|
+
# @return [Boolean]
|
2887
|
+
def superset?(other)
|
2888
|
+
other.subset?(self)
|
2889
|
+
end
|
2890
|
+
alias >= superset?
|
2891
|
+
|
2892
|
+
# Returns `true` if `other` contains all the items in this `Set`, plus at least
|
2893
|
+
# one item which is not in this set.
|
2894
|
+
#
|
2895
|
+
# @example
|
2896
|
+
# Immutable::Set[2, 3].proper_subset?(Immutable::Set[1, 2, 3]) # => true
|
2897
|
+
# Immutable::Set[1, 2, 3].proper_subset?(Immutable::Set[1, 2, 3]) # => false
|
2898
|
+
#
|
2899
|
+
# @param other [Set]
|
2900
|
+
# @return [Boolean]
|
2901
|
+
def proper_subset?(other)
|
2902
|
+
return false if other.size <= size
|
2903
|
+
# See comments above
|
2904
|
+
if other.size >= 150 && @trie.size >= 190 && !(other.is_a?(Immutable::Set) || other.is_a?(::Set))
|
2905
|
+
other = ::Set.new(other)
|
2906
|
+
end
|
2907
|
+
all? { |item| other.include?(item) }
|
2908
|
+
end
|
2909
|
+
alias < proper_subset?
|
2910
|
+
|
2911
|
+
# Returns `true` if this `Set` contains all the items in `other`, plus at least
|
2912
|
+
# one item which is not in `other`.
|
2913
|
+
#
|
2914
|
+
# @example
|
2915
|
+
# Immutable::Set[1, 2, 3].proper_superset?(Immutable::Set[2, 3]) # => true
|
2916
|
+
# Immutable::Set[1, 2, 3].proper_superset?(Immutable::Set[1, 2, 3]) # => false
|
2917
|
+
#
|
2918
|
+
# @param other [Set]
|
2919
|
+
# @return [Boolean]
|
2920
|
+
def proper_superset?(other)
|
2921
|
+
other.proper_subset?(self)
|
2922
|
+
end
|
2923
|
+
alias > proper_superset?
|
2924
|
+
|
2925
|
+
# Return `true` if this `Set` and `other` do not share any items.
|
2926
|
+
#
|
2927
|
+
# @example
|
2928
|
+
# Immutable::Set[1, 2].disjoint?(Immutable::Set[8, 9]) # => true
|
2929
|
+
#
|
2930
|
+
# @param other [Set]
|
2931
|
+
# @return [Boolean]
|
2932
|
+
def disjoint?(other)
|
2933
|
+
if other.size <= size
|
2934
|
+
other.each { |item| return false if include?(item) }
|
2935
|
+
else
|
2936
|
+
# See comment on #subset?
|
2937
|
+
if other.size >= 150 && @trie.size >= 190 && !(other.is_a?(Immutable::Set) || other.is_a?(::Set))
|
2938
|
+
other = ::Set.new(other)
|
2939
|
+
end
|
2940
|
+
each { |item| return false if other.include?(item) }
|
2941
|
+
end
|
2942
|
+
true
|
2943
|
+
end
|
2944
|
+
|
2945
|
+
# Return `true` if this `Set` and `other` have at least one item in common.
|
2946
|
+
#
|
2947
|
+
# @example
|
2948
|
+
# Immutable::Set[1, 2].intersect?(Immutable::Set[2, 3]) # => true
|
2949
|
+
#
|
2950
|
+
# @param other [Set]
|
2951
|
+
# @return [Boolean]
|
2952
|
+
def intersect?(other)
|
2953
|
+
!disjoint?(other)
|
2954
|
+
end
|
2955
|
+
|
2956
|
+
# Recursively insert the contents of any nested `Set`s into this `Set`, and
|
2957
|
+
# remove them.
|
2958
|
+
#
|
2959
|
+
# @example
|
2960
|
+
# Immutable::Set[Immutable::Set[1, 2], Immutable::Set[3, 4]].flatten
|
2961
|
+
# # => Immutable::Set[1, 2, 3, 4]
|
2962
|
+
#
|
2963
|
+
# @return [Set]
|
2964
|
+
def flatten
|
2965
|
+
reduce(self.class.empty) do |set, item|
|
2966
|
+
next set.union(item.flatten) if item.is_a?(Set)
|
2967
|
+
set.add(item)
|
2968
|
+
end
|
2969
|
+
end
|
2970
|
+
|
2971
|
+
alias group group_by
|
2972
|
+
alias classify group_by
|
2973
|
+
|
2974
|
+
# Return a randomly chosen item from this `Set`. If the set is empty, return `nil`.
|
2975
|
+
#
|
2976
|
+
# @example
|
2977
|
+
# Immutable::Set[1, 2, 3, 4, 5].sample # => 3
|
2978
|
+
#
|
2979
|
+
# @return [Object]
|
2980
|
+
def sample
|
2981
|
+
empty? ? nil : @trie.at(rand(size))[0]
|
2982
|
+
end
|
2983
|
+
|
2984
|
+
# Return an empty `Set` instance, of the same class as this one. Useful if you
|
2985
|
+
# have multiple subclasses of `Set` and want to treat them polymorphically.
|
2986
|
+
#
|
2987
|
+
# @return [Set]
|
2988
|
+
def clear
|
2989
|
+
self.class.empty
|
2990
|
+
end
|
2991
|
+
|
2992
|
+
# Return true if `other` has the same type and contents as this `Set`.
|
2993
|
+
#
|
2994
|
+
# @param other [Object] The object to compare with
|
2995
|
+
# @return [Boolean]
|
2996
|
+
def eql?(other)
|
2997
|
+
return true if other.equal?(self)
|
2998
|
+
return false if not instance_of?(other.class)
|
2999
|
+
other_trie = other.instance_variable_get(:@trie)
|
3000
|
+
return false if @trie.size != other_trie.size
|
3001
|
+
@trie.each do |key, _|
|
3002
|
+
return false if !other_trie.key?(key)
|
3003
|
+
end
|
3004
|
+
true
|
3005
|
+
end
|
3006
|
+
alias == eql?
|
3007
|
+
|
3008
|
+
# See `Object#hash`.
|
3009
|
+
# @return [Integer]
|
3010
|
+
def hash
|
3011
|
+
reduce(0) { |hash, item| (hash << 5) - hash + item.hash }
|
3012
|
+
end
|
3013
|
+
|
3014
|
+
# Return `self`. Since this is an immutable object duplicates are
|
3015
|
+
# equivalent.
|
3016
|
+
# @return [Set]
|
3017
|
+
def dup
|
3018
|
+
self
|
3019
|
+
end
|
3020
|
+
alias clone dup
|
3021
|
+
|
3022
|
+
undef :"<=>" # Sets are not ordered, so Enumerable#<=> will give a meaningless result
|
3023
|
+
undef :each_index # Set members cannot be accessed by 'index', so #each_index is not meaningful
|
3024
|
+
|
3025
|
+
# Return `self`.
|
3026
|
+
#
|
3027
|
+
# @return [self]
|
3028
|
+
def to_set
|
3029
|
+
self
|
3030
|
+
end
|
3031
|
+
|
3032
|
+
# @private
|
3033
|
+
def marshal_dump
|
3034
|
+
output = {}
|
3035
|
+
each do |key|
|
3036
|
+
output[key] = nil
|
3037
|
+
end
|
3038
|
+
output
|
3039
|
+
end
|
3040
|
+
|
3041
|
+
# @private
|
3042
|
+
def marshal_load(dictionary)
|
3043
|
+
@trie = dictionary.reduce(EmptyTrie) do |trie, key_value|
|
3044
|
+
trie.put(key_value.first, nil)
|
3045
|
+
end
|
3046
|
+
end
|
3047
|
+
|
3048
|
+
private
|
3049
|
+
|
3050
|
+
def new_trie(trie)
|
3051
|
+
if trie.empty?
|
3052
|
+
self.class.empty
|
3053
|
+
elsif trie.equal?(@trie)
|
3054
|
+
self
|
3055
|
+
else
|
3056
|
+
self.class.alloc(trie)
|
3057
|
+
end
|
3058
|
+
end
|
3059
|
+
end
|
3060
|
+
|
3061
|
+
# The canonical empty `Set`. Returned by `Set[]` when
|
3062
|
+
# invoked with no arguments; also returned by `Set.empty`. Prefer using this
|
3063
|
+
# one rather than creating many empty sets using `Set.new`.
|
3064
|
+
#
|
3065
|
+
# @private
|
3066
|
+
EmptySet = Immutable::Set.empty
|
3067
|
+
end
|