hamster 1.0.1.pre.rc2 → 1.0.1.pre.rc3

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