Skip to content

Commit

Permalink
Publish list-ops (#685)
Browse files Browse the repository at this point in the history
* remove wip status

* add vectors as prerequisite

* make instructions append more clear

* change 'init' parameter to 'acc'

* split into one deftest per test

* update instructions append

* rewrite example solution

* update starter file

* update instructions append

* add implementation for the optional goal (lists)
  • Loading branch information
tasxatzial authored Nov 12, 2024
1 parent 587a1e8 commit d94f4b5
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 120 deletions.
6 changes: 3 additions & 3 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1375,10 +1375,10 @@
"practices": [],
"prerequisites": [
"lists",
"closures"
"closures",
"vectors"
],
"difficulty": 5,
"status": "wip"
"difficulty": 5
}
]
},
Expand Down
21 changes: 16 additions & 5 deletions exercises/practice/list-ops/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -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!
6 changes: 3 additions & 3 deletions exercises/practice/list-ops/.meta/config.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"authors": [
"bobbicodes"
"bobbicodes",
"tasxatzial"
],
"contributors": [
"kahgoh",
"tasxatzial"
"kahgoh"
],
"files": {
"solution": [
Expand Down
80 changes: 48 additions & 32 deletions exercises/practice/list-ops/.meta/src/example.clj
Original file line number Diff line number Diff line change
@@ -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))))))
46 changes: 46 additions & 0 deletions exercises/practice/list-ops/.meta/src/example_lists.clj
Original file line number Diff line number Diff line change
@@ -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 ()))
36 changes: 18 additions & 18 deletions exercises/practice/list-ops/src/list_ops.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
146 changes: 87 additions & 59 deletions exercises/practice/list-ops/test/list_ops_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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]])))))

0 comments on commit d94f4b5

Please sign in to comment.