Skip to content

Commit

Permalink
Rename contains? and contains-key? to has-value? and has-key?
Browse files Browse the repository at this point in the history
Shorten docstrings to be less like a tutorial. They get put into RAM
and memory ain't free.
  • Loading branch information
bakpakin committed Jun 1, 2023
1 parent 0fcbda2 commit 5de8894
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 78 deletions.
48 changes: 9 additions & 39 deletions src/boot/boot.janet
Original file line number Diff line number Diff line change
Expand Up @@ -1197,45 +1197,15 @@
~(def ,alias :dyn ,;more ,kw))


(defn contains-key?
```Checks if a collection contains the specified key.
Semantically equivalent to `(not (nil? (get collection key)))`.
Arrays, tuples, and buffer types (string, buffer, keyword, symbol) are all indexed by integer keys.
For those types, this function simply checks if the index is less than the length.
If this function succeeds, then a call to `(in collection key)` is guarenteed
to succeed as well.
Note that tables or structs (dictionaries) never contain null keys```
[collection key]
(not (nil? (get collection key))))

(defn contains?
```Checks if a collection contains the specified value.
This supports any iterable type by way of the `next` function.
This includes buffers, dictionaries, arrays, fibers, and possibly abstract types.
For tables and structs, this checks the values, not the keys.
For arrays, tuples (and any other iterable type), this simply checks if any of the values are equal.
For buffer types (strings, buffers, keywords), this checks if the specified byte is present.
This is because, buffer types (strings, keywords, symbols) are simply sequences, with byte values.
This means they will also work with `next` and `index-of`.
However, it also means this function will not check for substrings, only integer bytes (which could be unexpected).
In other words is `(contains? "foo bar" "foo")` is always false, because "foo" is not an integer byte
If you want to check for a substring in a buffer, then use `(truthy? (string/find substr buffer))`,
or just `(if (string/find substr buffer) then else)`
In general this function has O(n) performance, since it requires iterating over all the values.
Note that tables or structs (dictionaries) never contain null values```
[collection val]
(not (nil? (index-of val collection))))

(defn has-key?
"Check if a data structure `ds` contains the key `key`."
[ds key]
(not= nil (get ds key)))

(defn has-value?
"Check if a data structure `ds` contains the value `value`. Will run in time proportional to the size of `ds`."
[ds value]
(not= nil (index-of value ds)))

(defdyn *defdyn-prefix* ``Optional namespace prefix to add to keywords declared with `defdyn`.
Use this to prevent keyword collisions between dynamic bindings.``)
Expand Down
78 changes: 39 additions & 39 deletions test/suite0010.janet
Original file line number Diff line number Diff line change
Expand Up @@ -33,44 +33,44 @@
(assert (= nil (index-of (chr "a") "")) "index-of 9")
(assert (= nil (index-of 10 @[])) "index-of 10")
(assert (= nil (index-of 10 @[1 2 3])) "index-of 11")
# NOTE: These is a motivation for the contains? and contains-key? functions below
# NOTE: These is a motivation for the has-value? and has-key? functions below

# returns false despite key present
(assert (= false (index-of 8 {true 7 false 8})) "index-of corner key (false) 1")
(assert (= false (index-of 8 @{false 8})) "index-of corner key (false) 2")
# still returns null
(assert (= nil (index-of 7 {false 8})) "index-of corner key (false) 3")

# contains?
(assert (= false (contains? [] "foo")) "contains? 1")
(assert (= true (contains? [4 7 1 3] 4)) "contains? 2")
(assert (= false (contains? [4 7 1 3] 22)) "contains? 3")
(assert (= false (contains? @[1 2 3] 4)) "contains? 4")
(assert (= true (contains? @[:a :b :c] :a)) "contains? 5")
(assert (= false (contains? {} :foo)) "contains? 6")
(assert (= true (contains? {:a :A :b :B} :A)) "contains? 7")
(assert (= true (contains? {:a :A :b :B} :A)) "contains? 7")
(assert (= true (contains? @{:a :A :b :B} :A)) "contains? 8")
(assert (= true (contains? "abc" (chr "a"))) "contains? 9")
(assert (= false (contains? "abc" "1")) "contains? 10")
# has-value?
(assert (= false (has-value? [] "foo")) "has-value? 1")
(assert (= true (has-value? [4 7 1 3] 4)) "has-value? 2")
(assert (= false (has-value? [4 7 1 3] 22)) "has-value? 3")
(assert (= false (has-value? @[1 2 3] 4)) "has-value? 4")
(assert (= true (has-value? @[:a :b :c] :a)) "has-value? 5")
(assert (= false (has-value? {} :foo)) "has-value? 6")
(assert (= true (has-value? {:a :A :b :B} :A)) "has-value? 7")
(assert (= true (has-value? {:a :A :b :B} :A)) "has-value? 7")
(assert (= true (has-value? @{:a :A :b :B} :A)) "has-value? 8")
(assert (= true (has-value? "abc" (chr "a"))) "has-value? 9")
(assert (= false (has-value? "abc" "1")) "has-value? 10")
# weird true/false corner cases, should align with "index-of corner key {k}" cases
(assert (= true (contains? {true 7 false 8} 8)) "contains? corner key (false) 1")
(assert (= true (contains? @{false 8} 8)) "contains? corner key (false) 2")
(assert (= false (contains? {false 8} 7)) "contains? corner key (false) 3")
(assert (= true (has-value? {true 7 false 8} 8)) "has-value? corner key (false) 1")
(assert (= true (has-value? @{false 8} 8)) "has-value? corner key (false) 2")
(assert (= false (has-value? {false 8} 7)) "has-value? corner key (false) 3")

# contains-key?
# has-key?
(do
(var test-contains-key-auto 0)
(defn test-contains-key [col key expected &keys {:name name}]
``Test that contains-key has the outcome `expected`, and that if
(var test-has-key-auto 0)
(defn test-has-key [col key expected &keys {:name name}]
``Test that has-key has the outcome `expected`, and that if
the result is true, then ensure (in key) does not fail either``
(assert (boolean? expected))
(default name (string "contains-key? " (++ test-contains-key-auto)))
(assert (= expected (contains-key? col key)) name)
(default name (string "has-key? " (++ test-has-key-auto)))
(assert (= expected (has-key? col key)) name)
(if
# guarenteed by `contains-key?` to never fail
# guarenteed by `has-key?` to never fail
expected (in col key)
# if `contains-key?` is false, then `in` should fail (for indexed types)
# if `has-key?` is false, then `in` should fail (for indexed types)
#
# For dictionary types, it should return nil
(let [[success retval] (protect (in col key))]
Expand All @@ -81,26 +81,26 @@
"%s: expected (in col key) to %s, but got %q"
name (if expected "succeed" "fail") retval)))))

(test-contains-key [] 0 false) # 1
(test-contains-key [4 7 1 3] 2 true) # 2
(test-contains-key [4 7 1 3] 22 false) # 3
(test-contains-key @[1 2 3] 4 false) # 4
(test-contains-key @[:a :b :c] 2 true) # 5
(test-contains-key {} :foo false) # 6
(test-contains-key {:a :A :b :B} :a true) # 7
(test-contains-key {:a :A :b :B} :A false) # 8
(test-contains-key @{:a :A :b :B} :a true) # 9
(test-contains-key "abc" 1 true) # 10
(test-contains-key "abc" 4 false) # 11
(test-has-key [] 0 false) # 1
(test-has-key [4 7 1 3] 2 true) # 2
(test-has-key [4 7 1 3] 22 false) # 3
(test-has-key @[1 2 3] 4 false) # 4
(test-has-key @[:a :b :c] 2 true) # 5
(test-has-key {} :foo false) # 6
(test-has-key {:a :A :b :B} :a true) # 7
(test-has-key {:a :A :b :B} :A false) # 8
(test-has-key @{:a :A :b :B} :a true) # 9
(test-has-key "abc" 1 true) # 10
(test-has-key "abc" 4 false) # 11
# weird true/false corner cases
#
# Tries to mimic the corresponding corner cases in contains? and index-of,
# Tries to mimic the corresponding corner cases in has-value? and index-of,
# but with keys/values inverted
#
# in the first two cases (truthy? (get val col)) would have given false negatives
(test-contains-key {7 true 8 false} 8 true :name "contains-key? corner value (false) 1")
(test-contains-key @{8 false} 8 true :name "contains-key? corner value (false) 2")
(test-contains-key @{8 false} 7 false :name "contains-key? corner value (false) 3"))
(test-has-key {7 true 8 false} 8 true :name "has-key? corner value (false) 1")
(test-has-key @{8 false} 8 true :name "has-key? corner value (false) 2")
(test-has-key @{8 false} 7 false :name "has-key? corner value (false) 3"))

# Regression
(assert (= {:x 10} (|(let [x $] ~{:x ,x}) 10)) "issue 463")
Expand Down

0 comments on commit 5de8894

Please sign in to comment.