diff --git a/config.json b/config.json index 8081877f..3c5fdba4 100644 --- a/config.json +++ b/config.json @@ -1375,10 +1375,10 @@ "practices": [], "prerequisites": [ "lists", - "closures" + "closures", + "vectors" ], - "difficulty": 5, - "status": "wip" + "difficulty": 5 } ] }, diff --git a/exercises/practice/list-ops/.docs/instructions.append.md b/exercises/practice/list-ops/.docs/instructions.append.md index e226d470..d27f6e13 100644 --- a/exercises/practice/list-ops/.docs/instructions.append.md +++ b/exercises/practice/list-ops/.docs/instructions.append.md @@ -1,8 +1,19 @@ # Instructions append -## Implementation tips +## Appendix -- The instructions use lists because they are synced with a shared repository to maintain consistency across tracks. - However, for this exercise in the Clojure track, you should assume that you are working with vectors instead of lists. -- It is important not to reuse existing Clojure built-in functions with similar functionality, as doing so would diminish the intended learning value of the exercise. - Some of these functions include `into`, `concat`, `cat`, `lazy-cat`, `mapcat`, `flatten`, `filter`, `filterv`, `remove`, `count`, `map`, `mapv`, `reduce`, `transduce`, `reverse`, and `rseq` from the **clojure.core** namespace. +The instructions are synced with a shared repository to ensure consistency across all language tracks. +For this exercise in the Clojure track, assume both the input and output are vectors. +As a stretch goal, consider how you could implement the solution without using lists anywhere in your code. +Also, think about the efficiency of your program. + +It is important not to reuse existing Clojure built-in functions with similar functionality, as doing so would diminish the intended learning value of the exercise. +Key functions from the **clojure.core** namespace to avoid include `into`, `concat`, `cat`, `lazy-cat`, `mapcat`, `flatten`, `filter`, `filterv`, `remove`, `count`, `map`, `mapv`, `reduce`, `transduce`, `reverse`, and `rseq`. + +### Optional goals + +Try to pass the tests by devising a solution that assumes both the input and output are lists instead of vectors. +The test suite does not need to be modified. +This time, consider how you could implement the solution without using vectors anywhere in your code. + +If you decide to publish this solution, be sure to include a comment indicating that it addresses the optional goal of using lists. Don't forget to update the docstrings! diff --git a/exercises/practice/list-ops/.meta/config.json b/exercises/practice/list-ops/.meta/config.json index 1035f4f3..f1b2c915 100644 --- a/exercises/practice/list-ops/.meta/config.json +++ b/exercises/practice/list-ops/.meta/config.json @@ -1,10 +1,10 @@ { "authors": [ - "bobbicodes" + "bobbicodes", + "tasxatzial" ], "contributors": [ - "kahgoh", - "tasxatzial" + "kahgoh" ], "files": { "solution": [ diff --git a/exercises/practice/list-ops/.meta/src/example.clj b/exercises/practice/list-ops/.meta/src/example.clj index c4bc083f..7aa1541c 100644 --- a/exercises/practice/list-ops/.meta/src/example.clj +++ b/exercises/practice/list-ops/.meta/src/example.clj @@ -1,34 +1,50 @@ (ns list-ops) -(defn foldl [f coll init] - (loop [acc init l coll] - (cond - (empty? l) acc - :else (recur (f acc (first l)) (rest l))))) - -(defn append [coll1 coll2] - (list-ops/foldl (fn [acc elem] (conj acc elem)) coll2 coll1)) - -(defn concatenate [coll] - (cond - (empty? coll) [] - :else (list-ops/foldl (fn [acc elem] (list-ops/append acc elem)) (rest coll) (first coll)))) - -(defn select-if [pred coll] - (loop [acc [] l coll] - (cond - (empty? l) acc - (pred (first l)) (recur (conj acc (first l)) (rest l)) - :else (recur acc (rest l))))) - -(defn length [coll] - (list-ops/foldl (fn [acc elem] (+ acc 1)) coll 0)) - -(defn apply-to-each [f coll] - (list-ops/foldl (fn [acc elem] (conj acc (f elem))) coll [])) - -(defn reverse-order [coll] - (list-ops/foldl (fn [acc elem] (cons elem acc)) coll [])) - -(defn foldr [f coll init] - (list-ops/foldl f (list-ops/reverse-order coll) init)) +(declare foldl) +(declare reverse-order) + +(defn append + [coll1 coll2] + (foldl conj coll2 coll1)) + +(defn concatenate + [colls] + (foldl append colls [])) + +(defn select-if + [pred coll] + (let [reducer (fn [acc el] + (if (pred el) + (conj acc el) + acc))] + (foldl reducer coll []))) + +(defn length + [coll] + (let [reducer (fn [acc _] + (inc acc))] + (foldl reducer coll 0))) + +(defn apply-to-each + [f coll] + (let [reducer (fn [acc el] + (conj acc (f el)))] + (foldl reducer coll []))) + +(defn foldl + [f coll acc] + (if (seq coll) + (recur f (rest coll) (f acc (first coll))) + acc)) + +(defn foldr + [f coll acc] + (foldl f (reverse-order coll) acc)) + +(defn reverse-order + [coll] + (loop [index (dec (length coll)) + result []] + (if (neg? index) + result + (recur (dec index) (conj result (coll index)))))) diff --git a/exercises/practice/list-ops/.meta/src/example_lists.clj b/exercises/practice/list-ops/.meta/src/example_lists.clj new file mode 100644 index 00000000..94b9c89e --- /dev/null +++ b/exercises/practice/list-ops/.meta/src/example_lists.clj @@ -0,0 +1,46 @@ +(ns list-ops) + +(declare foldl) +(declare reverse-order) + +(defn append + [coll1 coll2] + (reverse-order (foldl conj coll2 (reverse-order coll1)))) + +(defn concatenate + [colls] + (foldl append colls ())) + +(defn select-if + [pred coll] + (let [reducer (fn [acc el] + (if (pred el) + (conj acc el) + acc))] + (reverse-order (foldl reducer coll ())))) + +(defn length + [coll] + (let [reducer (fn [acc _] + (inc acc))] + (foldl reducer coll 0))) + +(defn apply-to-each + [f coll] + (let [reducer (fn [acc el] + (conj acc (f el)))] + (reverse-order (foldl reducer coll ())))) + +(defn foldl + [f coll acc] + (if (seq coll) + (recur f (rest coll) (f acc (first coll))) + acc)) + +(defn foldr + [f coll acc] + (foldl f (reverse-order coll) acc)) + +(defn reverse-order + [coll] + (foldl conj coll ())) diff --git a/exercises/practice/list-ops/src/list_ops.clj b/exercises/practice/list-ops/src/list_ops.clj index 28d30ea7..160992a0 100644 --- a/exercises/practice/list-ops/src/list_ops.clj +++ b/exercises/practice/list-ops/src/list_ops.clj @@ -3,47 +3,47 @@ (defn append "Given two vectors, it adds all the items in the second vector to the end of the first vector" [coll1 coll2] - ;; your code goes here -) + ;; function body + ) (defn concatenate "Given a vector of vectors, it combines all the vectors into one flattened vector" [colls] - ;; your code goes here -) + ;; function body + ) (defn select-if "Given a predicate and a vector, it returns the vector of all items for which predicate(item) is true" [pred coll] - ;; your code goes here -) + ;; function body + ) (defn length "Given a vector, it returns the number of items within it" [coll] - ;; your code goes here -) + ;; function body + ) (defn apply-to-each "Given a function and a vector, it returns the vector of the results of applying function(item) on all items" [f coll] - ;; your code goes here -) + ;; function body + ) (defn foldl "Given a function, a vector, and initial accumulator, it folds (reduces) each item into the accumulator from the left" - [f coll init] - ;; your code goes here -) + [f coll acc] + ;; function body + ) (defn foldr "Given a function, a vector, and an initial accumulator, it folds (reduces) each item into the accumulator from the right" - [f coll init] - ;; your code goes here -) + [f coll acc] + ;; function body + ) (defn reverse-order "Given a vector, it returns a vector with all the original items, but in reverse order" [coll] - ;; your code goes here -) + ;; function body + ) diff --git a/exercises/practice/list-ops/test/list_ops_test.clj b/exercises/practice/list-ops/test/list_ops_test.clj index 2b269075..83f05449 100644 --- a/exercises/practice/list-ops/test/list_ops_test.clj +++ b/exercises/practice/list-ops/test/list_ops_test.clj @@ -2,62 +2,90 @@ (:require [clojure.test :refer [deftest testing is]] list-ops)) -(deftest append-test - (testing "empty vectors" - (is (= [] (list-ops/append [] [])))) - (testing "vector to empty vector" - (is (= [1 2 3 4] (list-ops/append [] [1 2 3 4])))) - (testing "empty vector to vector" - (is (= [1 2 3 4] (list-ops/append [1 2 3 4] [])))) - (testing "non-empty vectors" - (is (= [1 2 2 3 4 5] (list-ops/append [1 2] [2 3 4 5]))))) - -(deftest concatenate-test - (testing "empty vector" - (is (= [] (list-ops/concatenate [])))) - (testing "vector of vectors" - (is (= [1 2 3 4 5 6] (list-ops/concatenate [[1 2] [3] [] [4 5 6]])))) - (testing "vector of nested vectors" - (is (= [[1] [2] [3] [] [4 5 6]] (list-ops/concatenate [[[1] [2]] [[3]] [[]] [[4 5 6]]]))))) - -(deftest select-if-test - (testing "empty vector" - (is (= [] (list-ops/select-if (fn [x] (= (mod x 2) 1)) []))) - (testing "non-empty vector" - (is (= [1 3 5] (list-ops/select-if (fn [x] (= (mod x 2) 1)) [1 2 3 5])))))) - -(deftest length-test - (testing "empty vector" - (is (= 0 (list-ops/length [])))) - (testing "non-empty vector" - (is (= 4 (list-ops/length [1 2 3 4]))))) - -(deftest apply-to-each-test - (testing "empty vector" - (is (= [] (list-ops/apply-to-each (fn [x] (+ x 1)) [])))) - (testing "non-empty vector" - (is (= [2 4 6 8] (list-ops/apply-to-each (fn [x] (+ x 1)) [1 3 5 7]))))) - -(deftest foldl-test - (testing "empty vector" - (is (= 2 (list-ops/foldl (fn [acc el] (* el acc)) [] 2)))) - (testing "direction independent function applied to non-empty vector" - (is (= 15 (list-ops/foldl (fn [acc el] (+ el acc)) [1 2 3 4] 5)))) - (testing "direction dependent function applied to non-empty vector" - (is (= 64 (list-ops/foldl (fn [acc el] (/ el acc)) [1 2 3 4] 24))))) - -(deftest foldr-test - (testing "empty vector" - (is (= 2 (list-ops/foldr (fn [acc el] (* el acc)) [] 2)))) - (testing "direction independent function applied to non-empty vector" - (is (= 15 (list-ops/foldr (fn [acc el] (+ el acc)) [1 2 3 4] 5)))) - (testing "direction dependent function applied to non-empty vector" - (is (= 9 (list-ops/foldr (fn [acc el] (/ el acc)) [1 2 3 4] 24))))) - -(deftest reverse-order-test - (testing "empty vector" - (is (= [] (list-ops/reverse-order [])))) - (testing "non-empty vector" - (is (= [7 5 3 1] (list-ops/reverse-order [1 3 5 7])))) - (testing "vector of vectors is not flattened" - (is (= [[4 5 6] [] [3] [1 2]] (list-ops/reverse-order [[1 2] [3] [] [4 5 6]]))))) +(deftest test-485b9452-bf94-40f7-a3db-c3cf4850066a + (testing "append entries to a vector and return the new vector -> empty vectors" + (is (= [] (list-ops/append [] []))))) + +(deftest test-2c894696-b609-4569-b149-8672134d340a + (testing "append entries to a vector and return the new vector -> vector to empty vector" + (is (= [1 2 3 4] (list-ops/append [] [1 2 3 4]))))) + +(deftest test-e842efed-3bf6-4295-b371-4d67a4fdf19c + (testing "append entries to a vector and return the new vector -> empty vector to vector" + (is (= [1 2 3 4] (list-ops/append [1 2 3 4] []))))) + +(deftest test-71dcf5eb-73ae-4a0e-b744-a52ee387922f + (testing "append entries to a vector and return the new vector -> non-empty vectors" + (is (= [1 2 2 3 4 5] (list-ops/append [1 2] [2 3 4 5]))))) + +(deftest test-28444355-201b-4af2-a2f6-5550227bde21 + (testing "concatenate a vector of vectors -> empty vector" + (is (= [] (list-ops/concatenate []))))) + +(deftest test-331451c1-9573-42a1-9869-2d06e3b389a9 + (testing "concatenate a vector of vectors -> vector of vectors" + (is (= [1 2 3 4 5 6] (list-ops/concatenate [[1 2] [3] [] [4 5 6]]))))) + +(deftest test-d6ecd72c-197f-40c3-89a4-aa1f45827e09 + (testing "concatenate a vector of vectors -> vector of nested vectors" + (is (= [[1] [2] [3] [] [4 5 6]] (list-ops/concatenate [[[1] [2]] [[3]] [[]] [[4 5 6]]]))))) + +(deftest test-0524fba8-3e0f-4531-ad2b-f7a43da86a16 + (testing "filter vector returning only values that satisfy the filter function -> empty vector" + (is (= [] (list-ops/select-if (fn [x] (= (mod x 2) 1)) []))))) + +(deftest test-88494bd5-f520-4edb-8631-88e415b62d24 + (testing "filter vector returning only values that satisfy the filter function -> non-empty vector" + (is (= [1 3 5] (list-ops/select-if (fn [x] (= (mod x 2) 1)) [1 2 3 5]))))) + +(deftest test-1cf0b92d-8d96-41d5-9c21-7b3c37cb6aad + (testing "returns the length of a vector -> empty vector" + (is (= 0 (list-ops/length []))))) + +(deftest test-d7b8d2d9-2d16-44c4-9a19-6e5f237cb71e + (testing "returns the length of a vector -> non-empty vector" + (is (= 4 (list-ops/length [1 2 3 4]))))) + +(deftest test-c0bc8962-30e2-4bec-9ae4-668b8ecd75aa + (testing "return a vector of elements whose values equal the vector value transformed by the mapping function -> empty vector" + (is (= [] (list-ops/apply-to-each (fn [x] (+ x 1)) []))))) + +(deftest test-11e71a95-e78b-4909-b8e4-60cdcaec0e91 + (testing "return a vector of elements whose values equal the vector value transformed by the mapping function -> non-empty vector" + (is (= [2 4 6 8] (list-ops/apply-to-each (fn [x] (+ x 1)) [1 3 5 7]))))) + +(deftest test-36549237-f765-4a4c-bfd9-5d3a8f7b07d2 + (testing "folds (reduces) the given vector from the left with a function -> empty vector" + (is (= 2 (list-ops/foldl (fn [acc el] (* el acc)) [] 2))))) + +(deftest test-7a626a3c-03ec-42bc-9840-53f280e13067 + (testing "folds (reduces) the given vector from the left with a function -> direction independent function applied to non-empty vector" + (is (= 15 (list-ops/foldl (fn [acc el] (+ el acc)) [1 2 3 4] 5))))) + +(deftest test-d7fcad99-e88e-40e1-a539-4c519681f390 + (testing "folds (reduces) the given vector from the left with a function -> direction dependent function applied to non-empty vector" + (is (= 64 (list-ops/foldl (fn [acc el] (/ el acc)) [1 2 3 4] 24))))) + +(deftest test-17214edb-20ba-42fc-bda8-000a5ab525b0 + (testing "folds (reduces) the given vector from the right with a function -> empty vector" + (is (= 2 (list-ops/foldr (fn [acc el] (* el acc)) [] 2))))) + +(deftest test-e1c64db7-9253-4a3d-a7c4-5273b9e2a1bd + (testing "folds (reduces) the given vector from the right with a function -> direction independent function applied to non-empty vector" + (is (= 15 (list-ops/foldr (fn [acc el] (+ el acc)) [1 2 3 4] 5))))) + +(deftest test-8066003b-f2ff-437e-9103-66e6df474844 + (testing "folds (reduces) the given vector from the right with a function -> direction dependent function applied to non-empty vector" + (is (= 9 (list-ops/foldr (fn [acc el] (/ el acc)) [1 2 3 4] 24))))) + +(deftest test-94231515-050e-4841-943d-d4488ab4ee30 + (testing "reverse the elements of the vector -> empty vector" + (is (= [] (list-ops/reverse-order []))))) + +(deftest test-fcc03d1e-42e0-4712-b689-d54ad761f360 + (testing "reverse the elements of the vector -> non-empty vector" + (is (= [7 5 3 1] (list-ops/reverse-order [1 3 5 7]))))) + +(deftest test-40872990-b5b8-4cb8-9085-d91fc0d05d26 + (testing "reverse the elements of the vector -> vector of vectors is not flattened" + (is (= [[4 5 6] [] [3] [1 2]] (list-ops/reverse-order [[1 2] [3] [] [4 5 6]])))))