immutable-ruby 0.0.1

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