Skip to content

Commit d3760a0

Browse files
committed
feat(list): add findAfter, findIf, search, lowerBound, upperBound, binarySearch, sorted? and minMax functions to std.List
1 parent 22e9c6f commit d3760a0

2 files changed

Lines changed: 265 additions & 4 deletions

File tree

‎List.ark‎

Lines changed: 188 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
(import std.Math)
2+
13
# @brief Reverse a given list and return a new one
24
# @details The original list is not modified
35
# @param list the list to reverse
@@ -8,7 +10,7 @@
810
(let reverse (fun (_L) (builtin__list:reverse _L)))
911

1012
# @brief Search an element in a List
11-
# @details The original list is not modified
13+
# @details The original list is not modified. Return -1 when not found
1214
# @param list the List to search in
1315
# @param value the element to search
1416
# =begin
@@ -18,6 +20,147 @@
1820
# @author https://github.com/SuperFola
1921
(let find (fun (_L _x) (builtin__list:find _L _x)))
2022

23+
# @brief Search an element in a List
24+
# @details The original List is not modified. Return -1 when not found
25+
# @param list the List to search in
26+
# @param x the element to search for
27+
# @param startIndex index to start searching from
28+
# =begin
29+
# (list:findAfter [1 2 3] 1 0) # 0
30+
# (list:findAfter [1 2 3] 1 1) # -1
31+
# =end
32+
# @author https://github.com/SuperFola
33+
(let findAfter (fun (_L _x _idx) (builtin__list:find _L _x _idx)))
34+
35+
# @brief Search an element in a List with a predicate
36+
# @details The original List is not modified. Return nil when not found
37+
# @param list the List to search in
38+
# @param pred unary Function returning a boolean
39+
# =begin
40+
# (list:findIf [1 2 3] math:even?) # 1
41+
# (list:findIf [1 3 5] math:even?) # nil
42+
# =end
43+
# @author https://github.com/SuperFola
44+
(let findIf (fun (_L _f) {
45+
(mut _i 0)
46+
(mut _output nil)
47+
(while (< _i (len _L)) {
48+
(if (_f (@ _L _i))
49+
{
50+
(set _output _i)
51+
(set _i (len _L)) })
52+
(set _i (+ 1 _i)) })
53+
_output }))
54+
55+
# @brief Search for a set of contiguous elements inside a list
56+
# @details The original List is not modified. Return nil when not found
57+
# @param list the List to search in
58+
# @param sub the elements to search for
59+
# =begin
60+
# (list:search [1 2 3 4 5 6] [2 3 4]) # 1
61+
# (list:search [1 2 3 4 5 6] [2 4]) # nil
62+
# =end
63+
# @author https://github.com/SuperFola
64+
(let search (fun ((ref _L) (ref _s)) {
65+
(assert (not (empty? _s)) "The sublist to look for can't be empty")
66+
(mut _i (find _L (head _s)))
67+
(if (!= -1 _i)
68+
{
69+
(mut _continue true)
70+
(mut _output nil)
71+
72+
(while _continue {
73+
(mut _ii _i)
74+
(mut _j 0)
75+
(mut _ok true)
76+
(while (and _ok (< _j (len _s)) (< _ii (len _L)))
77+
(if (!= (@ _L _ii) (@ _s _j))
78+
(set _ok false)
79+
{
80+
(set _j (+ 1 _j))
81+
(set _ii (+ 1 _ii)) }))
82+
(if (and _ok (= _j (len _s)))
83+
{
84+
(set _continue false)
85+
(set _output _i) })
86+
(if (>= _i (len _L))
87+
(set _continue false))
88+
(set _i (+ 1 _i)) })
89+
_output }
90+
nil) }))
91+
92+
# @brief Search for the first element in the list which is not ordered before value and return its index
93+
# @details The original List is not modified. Expect the List to be sorted. Return nil when not found
94+
# @param list the List to search in
95+
# @param x element to search for
96+
# =begin
97+
# (let prices [100.0 101.5 102.5 102.5 107.3])
98+
# (list:lowerBound prices 102.5) # 2
99+
# (list:lowerBound prices 110) # nil
100+
# =end
101+
# @author https://github.com/SuperFola
102+
(let lowerBound (fun ((ref _L) _x) {
103+
(mut _it nil)
104+
(mut _count (len _L))
105+
(mut _first 0)
106+
107+
(while (> _count 0) {
108+
(let _step (math:floordiv _count 2))
109+
(set _it (+ _first _step))
110+
(if (and (< _it (len _L)) (< (@ _L _it) _x))
111+
{
112+
(set _it (+ 1 _it))
113+
(set _first _it)
114+
(set _count (+ (- _count _step) 1)) }
115+
(set _count _step)) })
116+
117+
(if (< _first (len _L))
118+
_first
119+
nil) }))
120+
121+
# @brief Search for the first element in the list which is ordered after value and return its index
122+
# @details The original List is not modified. Expect the List to be sorted. Return nil when not found
123+
# @param list the List to search in
124+
# @param x element to search for
125+
# =begin
126+
# (let prices [100.0 101.5 102.5 102.5 107.3])
127+
# (list:upperBound prices 102.5) # 4
128+
# (list:upperBound prices 110) # nil
129+
# =end
130+
# @author https://github.com/SuperFola
131+
(let upperBound (fun ((ref _L) _x) {
132+
(mut _it nil)
133+
(mut _count (len _L))
134+
(mut _first 0)
135+
136+
(while (> _count 0) {
137+
(let _step (math:floordiv _count 2))
138+
(set _it (+ _first _step))
139+
(if (and (< _it (len _L)) (>= _x (@ _L _it)))
140+
{
141+
(set _it (+ 1 _it))
142+
(set _first _it)
143+
(set _count (+ (- _count _step) 1)) }
144+
(set _count _step)) })
145+
146+
(if (< _first (len _L))
147+
_first
148+
nil) }))
149+
150+
# @brief Check if an element is in a sorted List
151+
# @details The original List is not modified. Expect the List to be sorted
152+
# @param list the List to search in
153+
# @param x element to search for
154+
# =begin
155+
# (let prices [1 3 5 9])
156+
# (list:binarySearch prices 1) # true
157+
# (list:binarySearch prices 2) # false
158+
# =end
159+
# @author https://github.com/SuperFola
160+
(let binarySearch (fun ((ref _L) _x) {
161+
(let _first (lowerBound _L _x))
162+
(and (< _first (len _L)) (>= _x (@ _L _first))) }))
163+
21164
# @brief Search if an element is in a List
22165
# @details The original list is not modified
23166
# @param list the List to search in
@@ -62,6 +205,28 @@
62205
# @author https://github.com/SuperFola
63206
(let sort (fun (_L) (builtin__list:sort _L)))
64207

208+
# @brief Check if a List is sorted
209+
# @details The original list is not modified
210+
# @param list List to check
211+
# =begin
212+
# (list:sorted? [4 2 3]) # false
213+
# (list:sorted? []) # true
214+
# (list:sorted? [1]) # true
215+
# (list:sorted? [1 2 5]) # true
216+
# =end
217+
# @author https://github.com/SuperFola
218+
(let sorted? (fun ((ref _L)) {
219+
(mut _i 0)
220+
(mut _ok true)
221+
222+
(while (and _ok (< _i (- (len _L) 1))) {
223+
(set _ok (<= (@ _L _i) (@ _L (+ 1 _i))))
224+
(set _i (+ 1 _i)) })
225+
226+
(if (<= (len _L) 1)
227+
true
228+
_ok) }))
229+
65230
# @brief Generate a List of n copies of an element
66231
# @param count the number of copies
67232
# @param value the element to copy
@@ -263,7 +428,28 @@
263428
(set _index (+ 1 _index)) })
264429
_output }))
265430

266-
(import std.Math :min :max :even?)
431+
# @brief Find the minimum and the maximum in a list of numbers
432+
# @param _L list of numbers
433+
# @details The original list is not modified.
434+
# =begin
435+
# (let value (list:minMax [0 1 2 3 5 8])) # [0 8]
436+
# =end
437+
# @author https://github.com/SuperFola
438+
(let minMax (fun ((ref _L)) {
439+
(mut _index 0)
440+
(mut _min nil)
441+
(mut _max nil)
442+
443+
(while (< _index (len _L)) {
444+
(let _val (@ _L _index))
445+
446+
(if (or (nil? _min) (< _val _min))
447+
(set _min _val))
448+
(if (or (nil? _max) (> _val _max))
449+
(set _max _val))
450+
451+
(set _index (+ 1 _index)) })
452+
[_min _max] }))
267453

268454
# @brief Find the median in a list of numbers
269455
# @param _L list of numbers

‎tests/list-tests.ark‎

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@
3232
(test:eq (list:slice1 [1 2 3 4 5] 0 4) [1 2 3 4])
3333
(test:eq (list:slice1 [1 2 3 4 5] 0 1) [1]) })
3434

35+
(test:case "sorted?" {
36+
(test:expect (list:sorted? a))
37+
(test:expect (list:sorted? b))
38+
(test:expect (list:sorted? []))
39+
(test:expect (list:sorted? [1]))
40+
(test:expect (not (list:sorted? [3 2 1])))
41+
(test:expect (not (list:sorted? [1 3 2])))
42+
(test:expect (list:sorted? [1 1])) })
43+
3544
(test:case "zeros, ones" {
3645
(test:eq (list:zeros 0) [])
3746
(test:eq (list:zeros 1) [0])
@@ -41,6 +50,62 @@
4150
(test:eq (list:ones 1) [1])
4251
(test:eq (list:ones 5) [1 1 1 1 1]) })
4352

53+
(test:case "findAfter" {
54+
(test:eq (list:findAfter a 1 1) -1)
55+
(test:eq (list:findAfter a 1 0) 0)
56+
(test:eq (list:findAfter a 2 1) 1)
57+
(test:eq (list:findAfter [1 2 3 1] 1 1) 3)
58+
(test:eq (list:findAfter [] 0 1) -1) })
59+
60+
(test:case "findIf" {
61+
(test:eq (list:findIf [] even?) nil)
62+
(test:eq (list:findIf [1] even?) nil)
63+
(test:eq (list:findIf [1 3 5] even?) nil)
64+
(test:eq (list:findIf [1 2 3] even?) 1)
65+
(test:eq (list:findIf [1 2 3 4] even?) 1) })
66+
67+
(test:case "search" {
68+
(test:eq (list:search [1 2 3 4 5 6] [1 2 3 4 5 6]) 0)
69+
(test:eq (list:search [1 2 3 4 5 6] [1 2 3 4]) 0)
70+
(test:eq (list:search [1 2 3 4 5 6] [1 2 3]) 0)
71+
(test:eq (list:search [1 2 3 4 5 6] [2 3 4]) 1)
72+
(test:eq (list:search [1 2 3 4 5 6] [4 5 6]) 3)
73+
(test:eq (list:search [1 2 3 4 5 6] [6]) 5)
74+
(test:eq (list:search [1 2 3 4 5 6] [2 4]) nil)
75+
(test:eq (list:search [1 2 3 4 5 6] [2]) 1)
76+
(test:eq (list:search [] [2]) nil)
77+
(test:eq (list:search [1 1 1 1 1 1] [1]) 0)
78+
(test:eq (list:search [1 1 1 1 1 1] [1 1]) 0) })
79+
80+
(test:case "lowerBound, upperBound, binarySearch" {
81+
(test:eq (list:lowerBound [1 2 4 5 5 6] 0) 0)
82+
(test:eq (list:lowerBound [1 2 4 5 5 6] 1) 0)
83+
(test:eq (list:lowerBound [1 2 4 5 5 6] 2) 1)
84+
(test:eq (list:lowerBound [1 2 4 5 5 6] 3) 2)
85+
(test:eq (list:lowerBound [1 2 4 5 5 6] 4) 2)
86+
(test:eq (list:lowerBound [1 2 4 5 5 6] 5) 3)
87+
(test:eq (list:lowerBound [1 2 4 5 5 6] 6) 5)
88+
(test:eq (list:lowerBound [1 2 4 5 5 6] 7) nil)
89+
(test:eq (list:lowerBound [] 1) nil)
90+
(test:eq (list:lowerBound [100.0 101.5 102.5 102.5 107.3] 102.5) 2)
91+
(test:eq (list:lowerBound [100.0 101.5 102.5 102.5 107.3] 110) nil)
92+
93+
(test:eq (list:upperBound [1 2 4 5 5 6] 0) 0)
94+
(test:eq (list:upperBound [1 2 4 5 5 6] 1) 1)
95+
(test:eq (list:upperBound [1 2 4 5 5 6] 2) 2)
96+
(test:eq (list:upperBound [1 2 4 5 5 6] 3) 2)
97+
(test:eq (list:upperBound [1 2 4 5 5 6] 4) 3)
98+
(test:eq (list:upperBound [1 2 4 5 5 6] 5) 5)
99+
(test:eq (list:upperBound [1 2 4 5 5 6] 6) nil)
100+
(test:eq (list:upperBound [] 1) nil)
101+
(test:eq (list:upperBound [100.0 101.5 102.5 102.5 107.3] 102.5) 4)
102+
(test:eq (list:upperBound [100.0 101.5 102.5 102.5 107.3] 110) nil)
103+
104+
(test:eq (list:binarySearch [1 3 4 5 9] 1) true)
105+
(test:eq (list:binarySearch [1 3 4 5 9] 2) false)
106+
(test:eq (list:binarySearch [1 3 4 5 9] 3) true)
107+
(test:eq (list:binarySearch [] 3) false) })
108+
44109
(test:case "contains?" {
45110
(test:expect (list:contains? [1 2 3] 1))
46111
(test:expect (not (list:contains? [1 2 3] "1")))
@@ -51,17 +116,21 @@
51116
(test:eq (len [1 2 3]) 3) })
52117

53118
(test:case "forEach" {
119+
(mut count 0)
54120
(list:forEach a (fun (e) {
121+
(set count (+ 1 count))
55122
# just assert we have something, basically it's just a while + @
56-
(test:neq e nil) })) })
123+
(test:neq e nil) }))
124+
(test:eq count (len a)) })
57125

58126
(test:case "enumerate" {
59127
(mut position 0)
60128
(list:enumerate a (fun (idx e) {
61129
(test:eq idx position)
62130
(set position (+ 1 position))
63131
# just assert we have something, basically it's just a while + @
64-
(test:eq (type e) "Number") })) })
132+
(test:eq (type e) "Number") }))
133+
(test:eq position (len a)) })
65134

66135
(test:case "product" {
67136
(test:eq (list:product b) (* 4 5 6))
@@ -96,6 +165,12 @@
96165
(test:eq (list:max b) 6)
97166
(test:eq (list:max [-1]) -1) })
98167

168+
(test:case "minMax" {
169+
(test:eq (list:minMax []) [nil nil])
170+
(test:eq (list:minMax [1]) [1 1])
171+
(test:eq (list:minMax b) [4 6])
172+
(test:eq (list:minMax [-1]) [-1 -1]) })
173+
99174
(test:case "median" {
100175
(test:eq (list:median []) nil)
101176
(test:eq (list:median [1]) 1)

0 commit comments

Comments
 (0)