immutable-ruby 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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