Skip to content

Commit 42c2e85

Browse files
favilaswannodette
authored andcommitted
CLJS-2136: Clarify IFind contract to avoid double-lookups
* Adds a docstring to the -find method clarifying that implementors may need to return nil. * Changes find to test for and use IFind first rather than testing for key containment first. * Changes all IFind implementations in core to perform key checks and value retrieval in a single lookup. * Adds some more tests for find on a vector using degenerate keys. * Alters a test using find because of contact ambiguity to use -find instead.
1 parent 1f95b5e commit 42c2e85

File tree

3 files changed

+37
-32
lines changed

3 files changed

+37
-32
lines changed

src/main/cljs/cljs/core.cljs

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@
548548

549549
(defprotocol IFind
550550
"Protocol for implementing entry finding in collections."
551-
(-find [coll k]))
551+
(-find [coll k] "Returns the map entry for key, or nil if key not present."))
552552

553553
(defprotocol IMap
554554
"Protocol for adding mapping functionality to collections."
@@ -2242,11 +2242,11 @@ reduces them without incurring seq initialization"
22422242
(defn find
22432243
"Returns the map entry for key, or nil if key not present."
22442244
[coll k]
2245-
(when (and (not (nil? coll))
2246-
(associative? coll)
2247-
(contains? coll k))
2248-
(if (ifind? coll)
2249-
(-find coll k)
2245+
(if (ifind? coll)
2246+
(-find coll k)
2247+
(when (and (not (nil? coll))
2248+
(associative? coll)
2249+
(contains? coll k))
22502250
[k (get coll k)])))
22512251

22522252
(defn ^boolean distinct?
@@ -5231,8 +5231,9 @@ reduces them without incurring seq initialization"
52315231
false))
52325232

52335233
IFind
5234-
(-find [coll k]
5235-
[k (get coll k)])
5234+
(-find [coll n]
5235+
(when (and (== n (bit-or n 0)) (<= 0 n) (< n cnt))
5236+
[n (aget (unchecked-array-for coll n) (bit-and n 0x01f))]))
52365237

52375238
APersistentVector
52385239
IVector
@@ -5522,8 +5523,9 @@ reduces them without incurring seq initialization"
55225523
(throw (js/Error. "Subvec's key for assoc must be a number."))))
55235524

55245525
IFind
5525-
(-find [coll key]
5526-
[key (get coll key)])
5526+
(-find [coll n]
5527+
(when (<= end (+ start n))
5528+
(-find v (+ start n))))
55275529

55285530
IVector
55295531
(-assoc-n [coll n val]
@@ -6060,7 +6062,9 @@ reduces them without incurring seq initialization"
60606062

60616063
IFind
60626064
(-find [coll k]
6063-
[k (get coll k)])
6065+
(when (and ^boolean (goog/isString k)
6066+
(not (nil? (scan-array 1 k keys))))
6067+
[k (aget strobj k)]))
60646068

60656069
IKVReduce
60666070
(-kv-reduce [coll f init]
@@ -6289,10 +6293,10 @@ reduces them without incurring seq initialization"
62896293

62906294
IFind
62916295
(-find [node k]
6292-
(cond
6293-
(== k 0) [0 key]
6294-
(== k 1) [1 val]
6295-
:else nil))
6296+
(case k
6297+
0 [0 key]
6298+
1 [1 val]
6299+
nil))
62966300

62976301
IVector
62986302
(-assoc-n [node n v]
@@ -6505,7 +6509,8 @@ reduces them without incurring seq initialization"
65056509
IFind
65066510
(-find [coll k]
65076511
(let [idx (array-map-index-of coll k)]
6508-
[(aget arr idx) (get coll k)]))
6512+
(when-not (== idx -1)
6513+
[(aget arr idx) (aget arr (inc idx))])))
65096514

65106515
IMap
65116516
(-dissoc [coll k]
@@ -7949,10 +7954,10 @@ reduces them without incurring seq initialization"
79497954

79507955
IFind
79517956
(-find [node k]
7952-
(cond
7953-
(== k 0) [0 key]
7954-
(== k 1) [1 val]
7955-
:else nil))
7957+
(case k
7958+
0 [0 key]
7959+
1 [1 val]
7960+
nil))
79567961

79577962
IVector
79587963
(-assoc-n [node n v]
@@ -8110,10 +8115,10 @@ reduces them without incurring seq initialization"
81108115

81118116
IFind
81128117
(-find [node k]
8113-
(cond
8114-
(== k 0) [0 key]
8115-
(== k 1) [1 val]
8116-
:else nil))
8118+
(case k
8119+
0 [0 key]
8120+
1 [1 val]
8121+
nil))
81178122

81188123
IVector
81198124
(-assoc-n [node n v]

src/test/cljs/cljs/collections_test.cljs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@
3636
(is (= (find {} nil) nil))
3737
(is (= (find {:a 1} nil) nil))
3838
(is (= (find {:a 1 :b 2} nil) nil))
39-
(is (= (find [1 2 3] 0) [0 1])))
39+
(is (= (find [1 2 3] 0) [0 1]))
40+
(is (= (find [1 2 3] -1) nil))
41+
(is (= (find [1 2 3] js/NaN) nil))
42+
(is (= (find [1 2 3] 1.2) nil))
43+
(is (= (find [1 2 3] :a) nil))
44+
(is (= (find [1 2 3] 10) nil)))
4045
)
4146

4247
(deftest test-vectors

src/test/cljs/cljs/map_entry_test.cljs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,8 @@
113113
(are [x y] (= x y)
114114
[0 :key] (-find e 0)
115115
[1 :val] (-find e 1)
116-
;; Commented out as unsure about contract of -find
117-
;; in the case where key is not present.
118-
;nil (-find e 2)
119-
;nil (-find e -1)
120-
;; So testing `find` in this case instead as contract is clear.
121-
nil (find e 2)
122-
nil (find e -1))))
116+
nil (-find e 2)
117+
nil (-find e -1))))
123118

124119
(testing "IFn"
125120
(testing "-invoke 2-arity"

0 commit comments

Comments
 (0)