Repository: exercism/clojure
Pull request #682 (2024-10-09 12:15:28)

Add back generators (zipper, list-ops)
This PR adds back the edits to the generator files (zipper, list-ops) from the outdated list-ops PR (#552) by Bobbi since they have not been included in the recent PR (#623)

The commits have been cherry-picked from #552, but they don't include the edits to the list-ops exercise. Only the edits to the generator files have been transferred to the current PR.


Patch:
diff --git a/_src/clock_generator.clj b/_generators/clock_generator.clj
similarity index 100%
rename from _src/clock_generator.clj
rename to _generators/clock_generator.clj
diff --git a/_src/generator.clj b/_generators/generator.clj
similarity index 100%
rename from _src/generator.clj
rename to _generators/generator.clj
diff --git a/_generators/list-ops-generator.clj b/_generators/list-ops-generator.clj
new file mode 100644
index 0000000..59f7213
--- /dev/null
+++ b/_generators/list-ops-generator.clj
@@ -0,0 +1,177 @@
+#!/usr/bin/env bb
+
+(require '[cheshire.core :as json]
+         '[babashka.fs :as fs]
+         '[clojure.string :as str]
+         '[clojure.edn :as edn])
+
+(comment
+  (def slug "list-ops"))
+
+(def data
+  (let [url "https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/"]
+    {:canonical-data (json/parse-string (slurp (str url "/" slug "/canonical-data.json")) true)
+     :description (slurp (str url "/" slug "/description.md"))
+     :metadata (slurp (str url "/" slug "/metadata.toml"))}))
+
+(second
+ (str/split (:metadata data) #"="))
+
+(defn get-meta 
+  "Returns a vector containing the exercise title and blurb"
+  [data]
+  (mapv last
+       (map #(map str/trim (str/split % #"="))
+            (str/split-lines (:metadata data)))))
+
+(defn init-deps! [data]
+  (fs/create-dirs (fs/path "exercises" "practice"
+                           (:exercise (:canonical-data data)) "src"))
+  (spit (str (fs/file "exercises" "practice"
+                      (:exercise (:canonical-data data))
+                      "deps.edn"))
+        "{:aliases {:test {:extra-paths [\"test\"]
+                 :extra-deps {io.github.cognitect-labs/test-runner 
+                              {:git/url \"https://github.com/cognitect-labs/test-runner.git\"
+                               :sha \"705ad25bbf0228b1c38d0244a36001c2987d7337\"}}
+                 :main-opts [\"-m\" \"cognitect.test-runner\"]
+                 :exec-fn cognitect.test-runner.api/test}}}"))
+
+(comment
+  (init-deps! data)
+  )
+
+(defn init-lein! [data]
+  (let [slug (:exercise (:canonical-data data))]
+    (spit (str (fs/file "exercises" "practice"
+                        (:exercise (:canonical-data data)) "project.clj"))
+          (str "(defproject " slug " \"0.1.0-SNAPSHOT\"
+  :description \"" slug " exercise.\"
+  :url \"https://github.com/exercism/clojure/tree/main/exercises/" slug "\"
+  :dependencies [[org.clojure/clojure \"1.10.0\"]])
+"))))
+
+(comment
+  (init-lein! data)
+  )
+
+(defn test-ns-form [data]
+  (str "(ns " (:exercise data) "-test
+  (:require [clojure.test :refer [deftest testing is]]\n             "
+       (:exercise data) "))\n\n"))
+
+(defn src-ns-form [data]
+  (str "(ns " (:exercise data) ")\n\n"))
+
+(defn trans-fn [s]
+  (let [[args body] (str/split s #"->")
+        arg-strs (mapv str (edn/read-string args))
+        [arg1 op arg2] (str/split (str/trim body) #"\s")]
+    (str "(fn [" (apply str (interpose " " arg-strs)) "] " 
+         "(" op " " arg1 " " arg2 "))")))
+
+(comment
+  (trans-fn "(x) -> x + 1")  
+  (trans-fn "(x, y) -> x * y")
+  (trans-fn "(acc, el) -> el * acc")
+  )
+
+(defn testing-form [slug test-case]
+  (let [property (symbol (str slug "/" (:property test-case)))
+        input (:input test-case)
+        args (map #(get input %) (keys input))]
+    (str "  (testing \"" (:description test-case) "\"
+     (is (= " (:expected test-case) " "
+         (reverse (into (list property) args)) ")))")))
+
+(comment
+  (testing-form "list-ops" (first (:cases (first (:cases (:canonical-data data))))))
+  )
+
+(defn testing-forms
+  "Outputs a sequence of the test cases for a given property name
+   given its name as a string and the canonical data."
+  [property data]
+  (let [test-cases (filter #(= property (:property %))
+                           (mapcat :cases
+                                   (:cases (:canonical-data data))))]
+    (map #(testing-form (:exercise (:canonical-data data)) %) test-cases)))
+
+(comment
+  (testing-forms "append" data)
+  )
+
+(defn deftest-forms [data]
+  (for [property (distinct (map :property (mapcat :cases
+                                                  (:cases (:canonical-data data)))))]
+    (str "(deftest " property "-test\n"
+         (apply str (interpose "\n"
+                               (testing-forms property data)))
+         ")")))
+
+(comment
+  (deftest-forms data)
+  )
+
+(defn init-tests! [data]
+  (let [path (fs/path "exercises" "practice"
+                      (:exercise (:canonical-data data)) "test")]
+    (when-not (fs/directory? path)
+      (fs/create-dir path))
+    (spit (str (fs/file "exercises" "practice"
+                        (:exercise (:canonical-data data)) "test"
+                        (str (str/replace (:exercise (:canonical-data data)) "-" "_")
+                             "_test.clj")))
+          (str (test-ns-form (:canonical-data data))
+               (apply str (interpose "\n\n"
+                                     (deftest-forms data)))))))
+
+(comment
+  (init-tests! data)
+  )
+
+(defn init-src! [data]
+  (spit (str (fs/file "exercises" "practice" (:exercise (:canonical-data data)) "src"
+                      (str (str/replace (:exercise (:canonical-data data))
+                                        "-" "_") ".clj")))
+        (str (src-ns-form (:canonical-data data))
+             (apply str (interpose "\n\n"
+                                   (for [property (distinct (map :property (mapcat :cases
+                                                                                   (:cases (:canonical-data data)))))]
+                                     (str "(defn " property " []\n  )")))))))
+
+(comment
+  (init-src! data)
+  )
+
+(defn init-description! [data]
+  (let [path ["exercises" "practice" (:exercise (:canonical-data data)) ".docs"]]
+    (when-not (fs/directory? (apply fs/path path))
+      (fs/create-dir (apply fs/path path))
+      (spit (str (apply fs/file (conj path "instructions.md")))
+            (:description data)))))
+
+(comment
+  (init-description! data)
+  )
+
+(defn config [data author blurb]
+  (let [slug (:exercise (:canonical-data data))]
+    {:authors [author],
+     :contributors [],
+     :files {:solution [(str "src/" (str/replace slug "-" "_") ".clj")], 
+             :test [(str "test/" (str/replace slug "-" "_") "_test.clj")], 
+             :example [".meta/src/example.clj"]},
+     :blurb blurb}))
+
+(defn init-config! [data]
+  (let [path ["exercises" "practice" (:exercise (:canonical-data data)) ".meta"]]
+    (when-not (fs/directory? (apply fs/path path))
+   (fs/create-dirs (apply fs/path (conj path "src")))
+      (spit (str (apply fs/file (conj path "config.json")))
+            (json/generate-string (config data "porkostomus" (last (get-meta data)))
+                                  {:pretty true})))))
+
+(comment
+  (init-config! data)
+  )
\ No newline at end of file
diff --git a/generator.clj b/_generators/zipper-generator.clj
similarity index 96%
rename from generator.clj
rename to _generators/zipper-generator.clj
index 3c97c75..6ac5848 100644
--- a/generator.clj
+++ b/_generators/zipper-generator.clj
@@ -16,12 +16,12 @@
 (second
  (str/split (:metadata data) #"="))
 
-(defn get-meta 
+(defn get-meta
   "Returns a vector containing the exercise title and blurb"
   [data]
   (mapv last
-       (map #(map str/trim (str/split % #"="))
-            (str/split-lines (:metadata data)))))
+        (map #(map str/trim (str/split % #"="))
+             (str/split-lines (:metadata data)))))
 
 (defn init-deps [data]
   (fs/create-dirs (fs/path "exercises" "practice"
@@ -122,19 +122,18 @@
   (let [slug (:exercise (:canonical-data data))]
     {:authors [author],
      :contributors [],
-     :files {:solution [(str "src/" (str/replace slug "-" "_") ".clj")], 
-             :test [(str "test/" (str/replace slug "-" "_") "_test.clj")], 
+     :files {:solution [(str "src/" (str/replace slug "-" "_") ".clj")],
+             :test [(str "test/" (str/replace slug "-" "_") "_test.clj")],
              :example [".meta/src/example.clj"]},
      :blurb blurb}))
 
 (defn init-config! [data]
   (let [path ["exercises" "practice" (:exercise (:canonical-data data)) ".meta"]]
     (when-not (fs/directory? (apply fs/path path))
-   (fs/create-dirs (apply fs/path (conj path "src")))
+      (fs/create-dirs (apply fs/path (conj path "src")))
       (spit (str (apply fs/file (conj path "config.json")))
             (json/generate-string (config data "porkostomus" (last (get-meta data)))
                                   {:pretty true})))))
 
 (comment
-  (init-config! data)
-  )
\ No newline at end of file
+  (init-config! data))
\ No newline at end of file
diff --git a/config.json b/config.json
index 698bace..aacb72e 100644
--- a/config.json
+++ b/config.json
@@ -26,7 +26,7 @@
       "test/%{snake_slug}_test.clj"
     ],
     "example": [
-      "src/example.clj"
+      ".meta/src/example.clj"
     ],
     "exemplar": [
       ".meta/exemplar.clj"
@@ -122,7 +122,9 @@
           "sequential-destructuring"
         ],
         "prerequisites": [
-          "lists", "vectors", "strings"
+          "lists",
+          "vectors",
+          "strings"
         ],
         "status": "beta"
       },
@@ -138,8 +140,13 @@
         "slug": "squeaky-clean",
         "name": "Squeaky Clean",
         "uuid": "3af5b219-d751-4243-89f7-c8f05216f534",
-        "concepts": ["chars"],
-        "prerequisites": ["basics", "strings"],
+        "concepts": [
+          "chars"
+        ],
+        "prerequisites": [
+          "basics",
+          "strings"
+        ],
         "status": "beta"
       },
       {
@@ -154,16 +161,27 @@
         "slug": "coordinate-transformation",
         "name": "Coordinate Transformation",
         "uuid": "5b23eabd-6ebb-4fed-b889-9324ad174a5f",
-        "concepts": ["closures", "atoms"],
-        "prerequisites": ["vectors"],
+        "concepts": [
+          "closures",
+          "atoms"
+        ],
+        "prerequisites": [
+          "vectors"
+        ],
         "status": "beta"
       },
       {
         "slug": "card-games",
         "name": "Card Games",
         "uuid": "01a06d2b-ffb1-4497-9bb3-d93ee2d0931b",
-        "concepts": ["lists"],
-        "prerequisites": ["basics", "conditionals", "booleans"],
+        "concepts": [
+          "lists"
+        ],
+        "prerequisites": [
+          "basics",
+          "conditionals",
+          "booleans"
+        ],
         "status": "beta"
       }
     ],
@@ -172,17 +190,24 @@
         "slug": "two-fer",
         "name": "Two Fer",
         "uuid": "1706ec00-9e51-45d1-ac3e-01b0360ea950",
-        "practices": ["strings"],
-        "prerequisites": ["strings"],
-        "difficulty": 1,
-        "topics": null
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings"
+        ],
+        "difficulty": 1
       },
       {
         "slug": "armstrong-numbers",
         "name": "Armstrong Numbers",
         "uuid": "0e0fec77-651a-45a7-af99-a768ebebef05",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 1,
         "topics": [
           "math"
@@ -204,10 +229,13 @@
         "name": "Reverse String",
         "uuid": "0f7b1858-15d0-467d-9f94-56408249c2f7",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 1,
         "topics": [
-          "strings", "core_functions"
+          "strings",
+          "core_functions"
         ]
       },
       {
@@ -215,7 +243,9 @@
         "name": "Accumulate",
         "uuid": "49f62bbc-0f60-4922-b5a6-f266b80442f4",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 2,
         "topics": [
           "algorithms, core_functions"
@@ -225,19 +255,30 @@
         "slug": "acronym",
         "name": "Acronym",
         "uuid": "b99ed7da-98a8-43f0-8162-05c5408f6ac5",
-        "practices": ["strings"],
-        "prerequisites": ["strings", "booleans"],
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings",
+          "booleans"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "regular_expressions"
+          "strings",
+          "regular_expressions"
         ]
       },
       {
         "slug": "all-your-base",
         "name": "All Your Base",
         "uuid": "7b8c5f7e-3c7d-4e69-ba8c-f29bb492a628",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers", "vectors"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers",
+          "vectors"
+        ],
         "difficulty": 2,
         "topics": [
           "math"
@@ -247,8 +288,13 @@
         "slug": "anagram",
         "name": "Anagram",
         "uuid": "11818959-7e42-4a1d-8dfd-288cea744d66",
-        "practices": ["strings"],
-        "prerequisites": ["strings", "vectors"],
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings",
+          "vectors"
+        ],
         "difficulty": 2,
         "topics": [
           "strings"
@@ -258,19 +304,31 @@
         "slug": "bob",
         "name": "Bob",
         "uuid": "0317cc62-dd75-4841-86c3-01c055630fe7",
-        "practices": ["conditionals"],
-        "prerequisites": ["conditionals", "strings"],
+        "practices": [
+          "conditionals"
+        ],
+        "prerequisites": [
+          "conditionals",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "regular_expressions", "conditionals"
+          "strings",
+          "regular_expressions",
+          "conditionals"
         ]
       },
       {
         "slug": "collatz-conjecture",
         "name": "Collatz Conjecture",
         "uuid": "9516323c-721e-4bfc-84df-9bc32e467197",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers", "conditionals"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers",
+          "conditionals"
+        ],
         "difficulty": 2,
         "topics": [
           "math"
@@ -280,8 +338,12 @@
         "slug": "complex-numbers",
         "name": "Complex Numbers",
         "uuid": "ceaacb97-9c2c-43db-a9ac-28ebe6650f47",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 2,
         "topics": [
           "math"
@@ -292,10 +354,15 @@
         "name": "ETL",
         "uuid": "befca7ee-fc29-4ef0-afd7-2d3f68aa47fb",
         "practices": [],
-        "prerequisites": ["lists", "numbers", "strings"],
+        "prerequisites": [
+          "lists",
+          "numbers",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "integers", "maps"
+          "integers",
+          "maps"
         ]
       },
       {
@@ -303,18 +370,27 @@
         "name": "Hamming",
         "uuid": "d120ad9a-98e9-4de7-9cb6-6aef3101cd1c",
         "practices": [],
-        "prerequisites": ["numbers", "strings", "conditionals"],
+        "prerequisites": [
+          "numbers",
+          "strings",
+          "conditionals"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "conditionals"
+          "strings",
+          "conditionals"
         ]
       },
       {
         "slug": "nth-prime",
         "name": "Nth Prime",
         "uuid": "167113f2-b015-4c04-809d-670193758c25",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 2,
         "topics": [
           "math"
@@ -325,40 +401,61 @@
         "name": "Nucleotide Count",
         "uuid": "223d10d6-539d-441f-89ba-77f7743e6092",
         "practices": [],
-        "prerequisites": ["strings", "conditionals"],
+        "prerequisites": [
+          "strings",
+          "conditionals"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "conditionals", "exception_handling"
+          "strings",
+          "conditionals",
+          "exception_handling"
         ]
       },
       {
         "slug": "pangram",
         "name": "Pangram",
         "uuid": "1f96161b-c83a-4f66-bc50-3e32e035da1f",
-        "practices": ["strings"],
-        "prerequisites": ["strings", "booleans"],
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings",
+          "booleans"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "maps"
+          "strings",
+          "maps"
         ]
       },
       {
         "slug": "pig-latin",
         "name": "Pig Latin",
         "uuid": "a9b08dfd-3555-46bc-bc54-d605fd225c34",
-        "practices": ["strings"],
-        "prerequisites": ["strings"],
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "regular_expressions"
+          "strings",
+          "regular_expressions"
         ]
       },
       {
         "slug": "protein-translation",
         "name": "Protein Translation",
         "uuid": "591c3cc5-0d14-400d-85a2-4ce0e399813e",
-        "practices": ["strings"],
-        "prerequisites": ["strings", "vectors"],
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings",
+          "vectors"
+        ],
         "difficulty": 2,
         "topics": [
           "pattern_matching",
@@ -369,11 +466,19 @@
         "slug": "raindrops",
         "name": "Raindrops",
         "uuid": "e670c978-8b71-4880-a543-3f9fb28d88b3",
-        "practices": ["conditionals"],
-        "prerequisites": ["numbers", "strings", "conditionals"],
+        "practices": [
+          "conditionals"
+        ],
+        "prerequisites": [
+          "numbers",
+          "strings",
+          "conditionals"
+        ],
         "difficulty": 2,
         "topics": [
-          "integers", "strings", "conditionals"
+          "integers",
+          "strings",
+          "conditionals"
         ]
       },
       {
@@ -381,10 +486,13 @@
         "name": "RNA Transcription",
         "uuid": "d8773153-d717-43f1-9324-c7e8cf00455c",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "pattern_matching"
+          "strings",
+          "pattern_matching"
         ]
       },
       {
@@ -392,10 +500,16 @@
         "name": "Robot Name",
         "uuid": "ce7df719-d3e2-444f-8ae3-f81c89fd15ce",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "sets", "conditionals", "regular_expressions", "randomness"
+          "sets",
+          "conditionals",
+          "regular_expressions",
+          "randomness"
         ]
       },
       {
@@ -403,7 +517,10 @@
         "name": "Roman Numerals",
         "uuid": "cad43fab-50c2-470e-8364-6592e971d4df",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
           "conditionals"
@@ -414,10 +531,14 @@
         "name": "Rotational Cipher",
         "uuid": "a40e8d4b-9793-4991-87d8-7efb92a3e3f2",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "algorithms", "regular_expressions"
+          "algorithms",
+          "regular_expressions"
         ]
       },
       {
@@ -425,10 +546,15 @@
         "name": "Run-Length Encoding",
         "uuid": "fa1be98f-59b2-487e-948d-46e744d50dc4",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "regular_expressions", "parsing"
+          "strings",
+          "regular_expressions",
+          "parsing"
         ]
       },
       {
@@ -436,10 +562,14 @@
         "name": "Scrabble Score",
         "uuid": "2804d2d1-0029-4473-8431-0857d4767d91",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "maps", "strings"
+          "maps",
+          "strings"
         ]
       },
       {
@@ -447,10 +577,15 @@
         "name": "Secret Handshake",
         "uuid": "9c270d52-b1fe-4f1a-a639-2f7347c7cb92",
         "practices": [],
-        "prerequisites": ["numbers", "vectors", "strings"],
+        "prerequisites": [
+          "numbers",
+          "vectors",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "algorithms", "conditionals"
+          "algorithms",
+          "conditionals"
         ]
       },
       {
@@ -458,7 +593,10 @@
         "name": "Series",
         "uuid": "f342d3f2-775f-4fb6-93c2-9aea4a8f5f22",
         "practices": [],
-        "prerequisites": ["numbers", "vectors"],
+        "prerequisites": [
+          "numbers",
+          "vectors"
+        ],
         "difficulty": 2,
         "topics": [
           "strings"
@@ -468,8 +606,13 @@
         "slug": "space-age",
         "name": "Space Age",
         "uuid": "c97868e9-36ca-4d00-9483-0fc6b4c37231",
-        "practices": ["floating-point-numbers"],
-        "prerequisites": ["numbers", "floating-point-numbers"],
+        "practices": [
+          "floating-point-numbers"
+        ],
+        "prerequisites": [
+          "numbers",
+          "floating-point-numbers"
+        ],
         "difficulty": 2,
         "topics": [
           "math"
@@ -480,18 +623,27 @@
         "name": "Strain",
         "uuid": "8db4904c-a46f-4886-b214-0cf06b0f2ff1",
         "practices": [],
-        "prerequisites": ["conditionals", "numbers"],
+        "prerequisites": [
+          "conditionals",
+          "numbers"
+        ],
         "difficulty": 2,
         "topics": [
-          "algorithms", "conditionals", "filtering"
+          "algorithms",
+          "conditionals",
+          "filtering"
         ]
       },
       {
         "slug": "sublist",
         "name": "Sublist",
         "uuid": "85dc1645-8424-45f2-8653-bc9730887b40",
-        "practices": ["lists"],
-        "prerequisites": ["lists"],
+        "practices": [
+          "lists"
+        ],
+        "prerequisites": [
+          "lists"
+        ],
         "difficulty": 2,
         "topics": [
           "sets"
@@ -502,7 +654,10 @@
         "name": "Sum of Multiples",
         "uuid": "357de34a-c149-4a2c-b6a8-86ffb5eaa152",
         "practices": [],
-        "prerequisites": ["numbers", "conditionals"],
+        "prerequisites": [
+          "numbers",
+          "conditionals"
+        ],
         "difficulty": 2,
         "topics": [
           "math"
@@ -512,11 +667,18 @@
         "slug": "triangle",
         "name": "Triangle",
         "uuid": "a148a999-76ef-40d4-8bf7-96d6ae38876e",
-        "practices": ["floating-point-numbers", "conditionals"],
-        "prerequisites": ["floating-point-numbers", "conditionals"],
+        "practices": [
+          "floating-point-numbers",
+          "conditionals"
+        ],
+        "prerequisites": [
+          "floating-point-numbers",
+          "conditionals"
+        ],
         "difficulty": 2,
         "topics": [
-          "integers", "conditionals"
+          "integers",
+          "conditionals"
         ]
       },
       {
@@ -524,10 +686,15 @@
         "name": "Word Count",
         "uuid": "ea3cf3f6-35de-4da1-a74f-b7a13d89c39a",
         "practices": [],
-        "prerequisites": ["numbers", "conditionals", "strings"],
+        "prerequisites": [
+          "numbers",
+          "conditionals",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "regular_expressions", "strings"
+          "regular_expressions",
+          "strings"
         ]
       },
       {
@@ -535,10 +702,15 @@
         "name": "Atbash Cipher",
         "uuid": "7d37e817-d2b7-4554-8885-b02d57d1d788",
         "practices": [],
-        "prerequisites": ["numbers", "conditionals", "strings"],
+        "prerequisites": [
+          "numbers",
+          "conditionals",
+          "strings"
+        ],
         "difficulty": 3,
         "topics": [
-          "algorithms", "regular_expressions"
+          "algorithms",
+          "regular_expressions"
         ]
       },
       {
@@ -546,10 +718,15 @@
         "name": "Beer Song",
         "uuid": "56a4b06b-c48f-4a0f-883a-a08886472b56",
         "practices": [],
-        "prerequisites": ["numbers", "conditionals", "strings"],
+        "prerequisites": [
+          "numbers",
+          "conditionals",
+          "strings"
+        ],
         "difficulty": 3,
         "topics": [
-          "strings", "conditionals"
+          "strings",
+          "conditionals"
         ]
       },
       {
@@ -557,7 +734,9 @@
         "name": "Binary",
         "uuid": "daf3daed-61ef-4964-8d33-e7a739c7a470",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
           "math"
@@ -568,10 +747,14 @@
         "name": "Binary Search",
         "uuid": "f32c6a4b-0f23-4cd2-95b2-f6e70e4b40b1",
         "practices": [],
-        "prerequisites": ["vectors"],
+        "prerequisites": [
+          "vectors"
+        ],
         "difficulty": 3,
         "topics": [
-          "algorithms", "recursion", "conditionals"
+          "algorithms",
+          "recursion",
+          "conditionals"
         ]
       },
       {
@@ -579,10 +762,13 @@
         "name": "Binary Search Tree",
         "uuid": "53678d3d-de3f-483d-9f02-d2313290786d",
         "practices": [],
-        "prerequisites": ["vectors"],
+        "prerequisites": [
+          "vectors"
+        ],
         "difficulty": 3,
         "topics": [
-          "algorithms", "recursion"
+          "algorithms",
+          "recursion"
         ]
       },
       {
@@ -590,7 +776,10 @@
         "name": "Change",
         "uuid": "7945eb6a-20eb-489d-90bd-020df3d9e2cb",
         "practices": [],
-        "prerequisites": ["numbers", "vectors"],
+        "prerequisites": [
+          "numbers",
+          "vectors"
+        ],
         "difficulty": 3,
         "topics": [
           "algorithms"
@@ -601,10 +790,13 @@
         "name": "Flatten Array",
         "uuid": "6b81cf43-e071-4daf-89a9-d10e213a5751",
         "practices": [],
-        "prerequisites": ["vectors"],
+        "prerequisites": [
+          "vectors"
+        ],
         "difficulty": 3,
         "topics": [
-          "core_functions", "seqs"
+          "core_functions",
+          "seqs"
         ]
       },
       {
@@ -612,7 +804,9 @@
         "name": "Gigasecond",
         "uuid": "798445d8-acad-4b01-b471-3809f46a8a84",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
           "time"
@@ -623,10 +817,14 @@
         "name": "Grade School",
         "uuid": "a48ff289-baf0-4b13-a5aa-78f6ee91571b",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 3,
         "topics": [
-          "maps", "core_functions"
+          "maps",
+          "core_functions"
         ]
       },
       {
@@ -634,10 +832,13 @@
         "name": "Grains",
         "uuid": "2e57c7d4-d887-4dba-8483-60e026bdf5ea",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
-          "integers", "recursion"
+          "integers",
+          "recursion"
         ]
       },
       {
@@ -645,7 +846,9 @@
         "name": "Hexadecimal",
         "uuid": "a4b1b39a-d942-4921-9b67-2bd913ff52a0",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
           "math"
@@ -656,21 +859,31 @@
         "name": "ISBN Verifier",
         "uuid": "0479e73c-acb3-4a36-949c-a2065bfb57e5",
         "practices": [],
-        "prerequisites": ["strings", "booleans"],
+        "prerequisites": [
+          "strings",
+          "booleans"
+        ],
         "difficulty": 3,
         "topics": [
-          "regular_expressions", "conditionals"
+          "regular_expressions",
+          "conditionals"
         ]
       },
       {
         "slug": "isogram",
         "name": "Isogram",
         "uuid": "ced9928a-05dc-4afa-9645-a2f2e548fe6e",
-        "practices": ["strings"],
-        "prerequisites": ["strings", "booleans"],
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings",
+          "booleans"
+        ],
         "difficulty": 3,
         "topics": [
-          "filtering", "strings"
+          "filtering",
+          "strings"
         ]
       },
       {
@@ -678,10 +891,13 @@
         "name": "Kindergarten Garden",
         "uuid": "e40c52a7-9509-4a4d-9ad6-8ca7a3bdca1d",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 3,
         "topics": [
-          "strings", "conditionals"
+          "strings",
+          "conditionals"
         ]
       },
       {
@@ -689,10 +905,14 @@
         "name": "Leap",
         "uuid": "336aa5ec-f868-4a8a-9fd2-989b6c2fd0be",
         "practices": [],
-        "prerequisites": ["conditionals", "numbers"],
+        "prerequisites": [
+          "conditionals",
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
-          "integers", "conditionals"
+          "integers",
+          "conditionals"
         ]
       },
       {
@@ -700,7 +920,9 @@
         "name": "Pascal's Triangle",
         "uuid": "29b16274-b413-4231-a1c7-aff8f93c9b7e",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
           "math"
@@ -711,7 +933,9 @@
         "name": "Perfect Numbers",
         "uuid": "e10b7063-75ac-46d6-848c-c50db87bddc7",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
           "math"
@@ -722,7 +946,9 @@
         "name": "Phone Number",
         "uuid": "41b95380-2bba-407b-b400-7d8a627f18fa",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 3,
         "topics": [
           "parsing"
@@ -733,7 +959,9 @@
         "name": "Prime Factors",
         "uuid": "96005f0f-5153-4915-9f28-326b41b63fab",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
           "math"
@@ -744,21 +972,36 @@
         "name": "Proverb",
         "uuid": "540d93a9-707f-4043-b46c-1dc83f2d50d8",
         "practices": [],
-        "prerequisites": ["strings", "vectors"],
+        "prerequisites": [
+          "strings",
+          "vectors"
+        ],
         "difficulty": 3,
         "topics": [
-          "vectors", "strings", "conditionals"
+          "vectors",
+          "strings",
+          "conditionals"
         ]
       },
       {
         "slug": "say",
         "name": "Say",
         "uuid": "0ff7efbe-855e-4a81-9b64-f07d1557813f",
-        "practices": ["numbers", "strings", "conditionals"],
-        "prerequisites": ["numbers", "strings", "conditionals"],
+        "practices": [
+          "numbers",
+          "strings",
+          "conditionals"
+        ],
+        "prerequisites": [
+          "numbers",
+          "strings",
+          "conditionals"
+        ],
         "difficulty": 3,
         "topics": [
-          "strings", "integers", "exception_handling"
+          "strings",
+          "integers",
+          "exception_handling"
         ]
       },
       {
@@ -766,7 +1009,10 @@
         "name": "Trinary",
         "uuid": "f8a7f5eb-e317-4ffa-bdef-6e30a36511eb",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 3,
         "topics": [
           "math"
@@ -777,7 +1023,10 @@
         "name": "Allergies",
         "uuid": "a71bc471-8359-4019-8ad1-e456774388b7",
         "practices": [],
-        "prerequisites": ["vectors", "booleans"],
+        "prerequisites": [
+          "vectors",
+          "booleans"
+        ],
         "difficulty": 4,
         "topics": [
           "conditionals"
@@ -788,18 +1037,26 @@
         "name": "Crypto Square",
         "uuid": "7387b271-d765-497c-9b85-481c947439b6",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 4,
         "topics": [
-          "algorithms", "sorting", "regular_expressions"
+          "algorithms",
+          "sorting",
+          "regular_expressions"
         ]
       },
       {
         "slug": "difference-of-squares",
         "name": "Difference of Squares",
         "uuid": "6f69d8fe-9da2-48f9-a635-b9f54b626daf",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 4,
         "topics": [
           "math"
@@ -810,18 +1067,27 @@
         "name": "Dominoes",
         "uuid": "da04dfa4-5a66-49a5-a7ca-6320cfa4158f",
         "practices": [],
-        "prerequisites": ["numbers", "booleans"],
+        "prerequisites": [
+          "numbers",
+          "booleans"
+        ],
         "difficulty": 4,
         "topics": [
-          "graph_theory", "games"
+          "graph_theory",
+          "games"
         ]
       },
       {
         "slug": "largest-series-product",
         "name": "Largest Series Product",
         "uuid": "2978747b-86e6-48f0-aef2-2d00714c8c5e",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers", "strings"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 4,
         "topics": [
           "math"
@@ -832,18 +1098,25 @@
         "name": "Meetup",
         "uuid": "81e37fc7-13cd-46a9-987c-c9bda4ff1ead",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 4,
         "topics": [
-          "dates", "time"
+          "dates",
+          "time"
         ]
       },
       {
         "slug": "octal",
         "name": "Octal",
         "uuid": "fa596153-0cbe-40e9-a4d6-1a66fe6ef7e5",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 4,
         "topics": [
           "math"
@@ -854,7 +1127,11 @@
         "name": "Spiral Matrix",
         "uuid": "71d92de0-9f54-4e07-9a8f-fcd477f00219",
         "practices": [],
-        "prerequisites": ["vectors", "numbers", "conditionals"],
+        "prerequisites": [
+          "vectors",
+          "numbers",
+          "conditionals"
+        ],
         "difficulty": 4,
         "topics": [
           "recursivity"
@@ -865,10 +1142,13 @@
         "name": "Clock",
         "uuid": "83b7ffc2-aadd-4c26-9794-5e7f851eea8b",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 5,
         "topics": [
-          "dates", "time"
+          "dates",
+          "time"
         ]
       },
       {
@@ -876,7 +1156,9 @@
         "name": "Diamond",
         "uuid": "cc0d15ad-d562-458c-8d05-4b213693cfb9",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 5,
         "topics": [
           "transforming"
@@ -887,10 +1169,14 @@
         "name": "Luhn",
         "uuid": "e2d0287c-2fe9-4f46-abfb-4db855d7489b",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 5,
         "topics": [
-          "integers", "conditionals"
+          "integers",
+          "conditionals"
         ]
       },
       {
@@ -898,7 +1184,10 @@
         "name": "Sieve",
         "uuid": "0e87bb30-3e7e-49dc-9f80-2d291dfb0111",
         "practices": [],
-        "prerequisites": ["vectors", "numbers"],
+        "prerequisites": [
+          "vectors",
+          "numbers"
+        ],
         "difficulty": 5,
         "topics": [
           "math"
@@ -908,22 +1197,36 @@
         "slug": "robot-simulator",
         "name": "Robot Simulator",
         "uuid": "e69ecf2e-8629-4d20-a90f-6de7536247ce",
-        "practices": ["conditionals"],
-        "prerequisites": ["conditionals"],
+        "practices": [
+          "conditionals"
+        ],
+        "prerequisites": [
+          "conditionals"
+        ],
         "difficulty": 6,
         "topics": [
-          "strings", "conditionals"
+          "strings",
+          "conditionals"
         ]
       },
       {
         "slug": "wordy",
         "name": "Wordy",
         "uuid": "5528f567-2b5b-498a-b166-984b2c90f6a5",
-        "practices": ["numbers", "strings", "conditionals"],
-        "prerequisites": ["numbers", "strings", "conditionals"],
+        "practices": [
+          "numbers",
+          "strings",
+          "conditionals"
+        ],
+        "prerequisites": [
+          "numbers",
+          "strings",
+          "conditionals"
+        ],
         "difficulty": 6,
         "topics": [
-          "regular_expressions", "parsing"
+          "regular_expressions",
+          "parsing"
         ]
       },
       {
@@ -931,7 +1234,10 @@
         "name": "Bank Account",
         "uuid": "d67a7a46-050c-4472-84bd-2e216079a452",
         "practices": [],
-        "prerequisites": ["conditionals", "numbers"],
+        "prerequisites": [
+          "conditionals",
+          "numbers"
+        ],
         "difficulty": 7,
         "topics": [
           "conditionals"
@@ -942,7 +1248,10 @@
         "name": "Matching Brackets",
         "uuid": "86f005c6-1754-4907-becb-a0997fd52d05",
         "practices": [],
-        "prerequisites": ["strings", "booleans"],
+        "prerequisites": [
+          "strings",
+          "booleans"
+        ],
         "difficulty": 7,
         "topics": [
           "parsing"
@@ -953,10 +1262,15 @@
         "name": "Minesweeper",
         "uuid": "e07e545c-997f-424b-b4ca-34fd7cc06bf7",
         "practices": [],
-        "prerequisites": ["vectors", "conditionals", "strings"],
+        "prerequisites": [
+          "vectors",
+          "conditionals",
+          "strings"
+        ],
         "difficulty": 7,
         "topics": [
-          "algorithms", "games"
+          "algorithms",
+          "games"
         ]
       },
       {
@@ -964,7 +1278,10 @@
         "name": "Poker",
         "uuid": "f842d531-e2a1-4573-90c3-b77177df4787",
         "practices": [],
-        "prerequisites": ["conditionals", "strings"],
+        "prerequisites": [
+          "conditionals",
+          "strings"
+        ],
         "difficulty": 7,
         "topics": [
           "games"
@@ -975,10 +1292,15 @@
         "name": "Queen Attack",
         "uuid": "edd7932d-50a6-4350-b945-2bb8a8c891c7",
         "practices": [],
-        "prerequisites": ["booleans", "conditionals", "numbers"],
+        "prerequisites": [
+          "booleans",
+          "conditionals",
+          "numbers"
+        ],
         "difficulty": 7,
         "topics": [
-          "equality", "conditionals"
+          "equality",
+          "conditionals"
         ]
       },
       {
@@ -986,7 +1308,9 @@
         "name": "Go Counting",
         "uuid": "f0210181-7fe5-49f9-9036-be4256612b3e",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 9,
         "topics": [
           "games"
@@ -997,7 +1321,10 @@
         "name": "POV",
         "uuid": "ca557f64-9e2d-4ab0-8788-4133764ef703",
         "practices": [],
-        "prerequisites": ["strings", "vectors"],
+        "prerequisites": [
+          "strings",
+          "vectors"
+        ],
         "difficulty": 10,
         "topics": [
           "conditionals"
@@ -1008,7 +1335,10 @@
         "name": "Yacht",
         "uuid": "459c8468-efc4-46a6-bbc0-9937167827a7",
         "practices": [],
-        "prerequisites": ["strings", "vectors"],
+        "prerequisites": [
+          "strings",
+          "vectors"
+        ],
         "difficulty": 5,
         "topics": [
           "conditionals"
@@ -1019,11 +1349,26 @@
         "name": "Zipper",
         "uuid": "331b2960-baae-4331-a462-a434fd6e8767",
         "practices": [],
-        "prerequisites": ["strings", "vectors"],
+        "prerequisites": [
+          "strings",
+          "vectors"
+        ],
         "difficulty": 9,
         "topics": [
           "conditionals"
         ]
+      },
+      {
+        "slug": "list-ops",
+        "name": "List Ops",
+        "uuid": "699fb4ec-210d-454d-a41a-f7014abf8423",
+        "practices": [],
+        "prerequisites": [
+          "lists",
+          "closures"
+        ],
+        "difficulty": 5,
+        "status": "wip"
       }
     ]
   },
@@ -1127,14 +1472,14 @@
     }
   ],
   "tags": [
+    "execution_mode/compiled",
     "paradigm/declarative",
     "paradigm/functional",
-    "typing/dynamic",
-    "execution_mode/compiled",
-    "platform/windows",
-    "platform/mac",
     "platform/linux",
+    "platform/mac",
+    "platform/windows",
     "runtime/jvm",
+    "typing/dynamic",
     "used_for/artificial_intelligence",
     "used_for/backends",
     "used_for/cross_platform_development",
diff --git a/exercises/concept/bird-watcher/.docs/hints.md b/exercises/concept/bird-watcher/.docs/hints.md
index e32ffb0..7dd02b2 100644
--- a/exercises/concept/bird-watcher/.docs/hints.md
+++ b/exercises/concept/bird-watcher/.docs/hints.md
@@ -6,7 +6,7 @@
 
 ## 2. Check how many birds visited today
 
-- The [`last`][last] function can be used to return the last item in a sequential collection.
+- The [`peek`][peek] function can be used to return the last item in a vector.
 
 ## 3. Increment today's count
 
@@ -20,13 +20,13 @@
 
 - The [`every?`][every?] function can be used to check if all items in a collection satisfy a given predicate.
 
-- The [`pos?`][pos?] predicate returns true if a number is greater than zero.
+- The [`pos?`][pos?] predicate returns `true` if a number is greater than zero.
 
-- The [`not`][not] function returns true if an expression evaluates to logical false.
+- The [`not`][not] function returns `true` if an expression evaluates to logical false.
 
 ## 5. Calculate the number of visiting birds for the first number of days
 
-- The [`take`][take] function can be used return a sequence of the first n items in a collection.
+- The [`take`][take] function can be used to return a sequence of the first n items in a collection.
 
 - The [`reduce`][reduce] function can be used to add a collection of values together.
 
@@ -36,7 +36,7 @@
 
 ## 7.  Check for odd week
 
-- The [`=`][equality] function can be used to test equality.
+- The [`=`][equality] function can be used to test for equality.
 
 [count]: https://clojuredocs.org/clojure.core/count
 [dec]: https://clojuredocs.org/clojure.core/dec
@@ -44,7 +44,7 @@
 [every?]: https://clojuredocs.org/clojure.core/every_q
 [filter]: https://clojuredocs.org/clojure.core/filter
 [inc]: https://clojuredocs.org/clojure.core/inc
-[last]: https://clojuredocs.org/clojure.core/last
+[peek]: https://clojuredocs.org/clojure.core/peek
 [not]: https://clojuredocs.org/clojure.core/not
 [pos?]: https://clojuredocs.org/clojure.core/pos_q
 [reduce]: https://clojuredocs.org/clojure.core/reduce
diff --git a/exercises/concept/bird-watcher/.docs/instructions.md b/exercises/concept/bird-watcher/.docs/instructions.md
index a3ce8b3..121782c 100755
--- a/exercises/concept/bird-watcher/.docs/instructions.md
+++ b/exercises/concept/bird-watcher/.docs/instructions.md
@@ -62,7 +62,7 @@ Implement the `busy-days` function to return the number of busy days:
 
 ## 7. Check for odd week
 
-Over the last year, you've found that some weeks for the same, odd pattern, where the counts alternate between one and zero birds visiting. Implement the `odd-week?` function that returns `true` if the bird count pattern of this week matches the odd pattern:
+Over the last year, you've found that some weeks form the same, odd pattern, where the counts alternate between one and zero birds visiting. Implement the `odd-week?` function that returns `true` if the bird count pattern of this week matches the odd pattern:
 
 ```clojure
 (odd-week? [1 0 1 0 1 0 1])
diff --git a/exercises/concept/bird-watcher/.meta/exemplar.clj b/exercises/concept/bird-watcher/.meta/exemplar.clj
index 7473988..cfdb65a 100644
--- a/exercises/concept/bird-watcher/.meta/exemplar.clj
+++ b/exercises/concept/bird-watcher/.meta/exemplar.clj
@@ -3,13 +3,13 @@
 (def last-week [0 2 5 3 7 8 4])
 
 (defn today [birds]
-  (last birds))
+  (peek birds))
 
 (defn inc-bird [birds]
   (update birds (dec (count birds)) inc))
 
 (defn day-without-birds? [birds]
-  (pos? (count (filter zero? birds))))
+  (not (every? pos? birds)))
 
 (defn n-days-count [birds n]
   (reduce + (take n birds)))
diff --git a/exercises/concept/card-games/.docs/hints.md b/exercises/concept/card-games/.docs/hints.md
index 3013115..d37b8cc 100644
--- a/exercises/concept/card-games/.docs/hints.md
+++ b/exercises/concept/card-games/.docs/hints.md
@@ -11,18 +11,18 @@
 ## 3. Finding Prior Rounds
 
 - The [`some`][some] function will return the first logically true value in a collection.
-- [`boolean`][boolean] is useful in cases where a function returns a value but the tests are expecting a boolean.
+- The [`boolean`][boolean] function is useful in cases where a function returns a value but the tests are expecting a boolean.
 
 ## 4. Averaging Card Values
 
 - Recall basic arithmetic operators such as [`/`][division] and [`+`][addition].
 - A collection of numbers can be summed using either [`apply`][apply] or [`reduce`][reduce].
 - The [`count`][count] function returns the number of items in a collection.
-- The `/` function returns a ratio type when operating on integers. You can use [`double`][double] to convert a ratio to a decimal.
+- The [`/`][division] function returns a ratio type when operating on integers. You can use [`double`][double] to convert a ratio to a decimal.
 
 ## 5. Alternate Averages
 
-- There are sequence functions to return the [`first`][first] or [`last`] element of a sequential collection.
+- There are sequence functions to return the [`first`][first] or [`last`][last] element of a sequential collection.
 - To find the middle card you can divide the number of cards by 2, round to the lowest integer with [`int`][int], and retrieve the card at that index using [`nth`][nth].
 - Use [`or`][or] to determine if either condition is true.
 
diff --git a/exercises/practice/complex-numbers/.docs/instructions.append.md b/exercises/practice/complex-numbers/.docs/instructions.append.md
new file mode 100644
index 0000000..1c6bfad
--- /dev/null
+++ b/exercises/practice/complex-numbers/.docs/instructions.append.md
@@ -0,0 +1,4 @@
+# Instructions append
+
+**Note:** The instructions ask for the exponent function because they are synced with a shared repository to maintain consistency across all language tracks.
+However, for this exercise in the Clojure track, you don't need to implement it.
diff --git a/exercises/practice/leap/.approaches/boolean-operations/content.md b/exercises/practice/leap/.approaches/boolean-operations/content.md
index 01a049d..0bd137b 100644
--- a/exercises/practice/leap/.approaches/boolean-operations/content.md
+++ b/exercises/practice/leap/.approaches/boolean-operations/content.md
@@ -11,7 +11,7 @@
 ```
 
 At the core of this approach, three checks are returning three boolean values.
-We can use [Boolean logic](https://en.wikipedia.org/wiki/Boolean_algebra) to combine the results. Multiple variations are possible. One is shown above, but the below is also possible.
+We can use [Boolean logic](https://en.wikipedia.org/wiki/Boolean_algebra) to combine the results. Multiple variations are possible. One is shown above, but the one below is also possible.
 
 ```clojure
 (defn leap-year? [year]
@@ -22,7 +22,7 @@ We can use [Boolean logic](https://en.wikipedia.org/wiki/Boolean_algebra) to com
 
 ## Being explicit
 
-The above examples use a private function `divides?` to be more explicit, to show what logical check is done rather than what operation is performed.
+The above examples use a private function `divides?` to be more explicit, showing what logical check is done rather than what operation is performed.
 This is common in Clojure code, but it is also possible to do the checks directly.
 
 ```clojure
@@ -34,7 +34,7 @@ This is common in Clojure code, but it is also possible to do the checks directl
 
 In this example, in addition, instead of negating the second check, we check if the reminder from the division is greater than zero.
 
-If the concern is that defining `divides?` adds confusion, because it is not known in the only place where it is used, `letfn` can be used. Here is another example.
+If the concern is that defining `divides?` adds confusion because it is not known in the only place where it is used, `letfn` can be used instead. Here is another example.
 
 ```clojure
 (defn leap-year? [year]
diff --git a/exercises/practice/leap/.approaches/flow-control/content.md b/exercises/practice/leap/.approaches/flow-control/content.md
index e65b7a7..1bf0618 100644
--- a/exercises/practice/leap/.approaches/flow-control/content.md
+++ b/exercises/practice/leap/.approaches/flow-control/content.md
@@ -39,7 +39,7 @@ Another option is `cond` which allows for evaluating multiple conditions, simila
     :else false))
 ```
 
-A very similar alternative is to use the `condp` macro, which takes a predicate and applies it to a series of test value and expected result pairs.
+A very similar alternative is to use the `condp` macro, which takes a predicate and applies it to a series of test values and expected result pairs.
 
 ```clojure
 (defn leap-year? [year]
@@ -50,7 +50,7 @@ A very similar alternative is to use the `condp` macro, which takes a predicate
         false))
 ```
 
-When using both `cond` and `condp,` the other matters as the first true condition stops evaluating the list and determines the result.
+When using both `cond` and `condp,` the order matters, as the first true condition stops evaluating the list and determines the result.
 
 ## Case
 
diff --git a/exercises/practice/leap/.approaches/introduction.md b/exercises/practice/leap/.approaches/introduction.md
index 990fcf4..07b559b 100644
--- a/exercises/practice/leap/.approaches/introduction.md
+++ b/exercises/practice/leap/.approaches/introduction.md
@@ -44,7 +44,7 @@ Explore the details in the [boolean operations approach][boolean-approach].
 
 ## Approach: conditional branching
 
-Instead of combining the logical expressions, we can use conditional branching with functions like `if` or `cond` to perform the necessary checks and determin if a given years is a leap one. 
+Instead of combining the logical expressions, we can use conditional branching with functions like `if` or `cond` to perform the necessary checks and determine if a given year is a leap year.
 
 ```clojure
 (if (divides? year 100) 
@@ -56,7 +56,7 @@ In the [flow control expressions approach][flow-control-approach], we discuss th
 
 ## Approach: data shapes
 
-This is a little less straight forward approach, but with a little bit of setup we can get to a `leap-year?` function that looks like so:
+This is a slightly less straightforward approach, but with a bit of setup, we can get to a `leap-year?` function that looks like so:
 
 ```clojure
 (defn leap-year? [year]
diff --git a/exercises/practice/list-ops/.docs/instructions.append.md b/exercises/practice/list-ops/.docs/instructions.append.md
new file mode 100644
index 0000000..e226d47
--- /dev/null
+++ b/exercises/practice/list-ops/.docs/instructions.append.md
@@ -0,0 +1,8 @@
+# Instructions append
+
+## Implementation tips
+
+- 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.
diff --git a/exercises/practice/list-ops/.docs/instructions.md b/exercises/practice/list-ops/.docs/instructions.md
new file mode 100644
index 0000000..ebc5dff
--- /dev/null
+++ b/exercises/practice/list-ops/.docs/instructions.md
@@ -0,0 +1,19 @@
+# Instructions
+
+Implement basic list operations.
+
+In functional languages list operations like `length`, `map`, and `reduce` are very common.
+Implement a series of basic list operations, without using existing functions.
+
+The precise number and names of the operations to be implemented will be track dependent to avoid conflicts with existing names, but the general operations you will implement include:
+
+- `append` (_given two lists, add all items in the second list to the end of the first list_);
+- `concatenate` (_given a series of lists, combine all items in all lists into one flattened list_);
+- `filter` (_given a predicate and a list, return the list of all items for which `predicate(item)` is True_);
+- `length` (_given a list, return the total number of items within it_);
+- `map` (_given a function and a list, return the list of the results of applying `function(item)` on all items_);
+- `foldl` (_given a function, a list, and initial accumulator, fold (reduce) each item into the accumulator from the left_);
+- `foldr` (_given a function, a list, and an initial accumulator, fold (reduce) each item into the accumulator from the right_);
+- `reverse` (_given a list, return a list with all the original items, but in reversed order_).
+
+Note, the ordering in which arguments are passed to the fold functions (`foldl`, `foldr`) is significant.
diff --git a/exercises/practice/list-ops/.meta/config.json b/exercises/practice/list-ops/.meta/config.json
new file mode 100644
index 0000000..1035f4f
--- /dev/null
+++ b/exercises/practice/list-ops/.meta/config.json
@@ -0,0 +1,21 @@
+{
+  "authors": [
+    "bobbicodes"
+  ],
+  "contributors": [
+    "kahgoh",
+    "tasxatzial"
+  ],
+  "files": {
+    "solution": [
+      "src/list_ops.clj"
+    ],
+    "test": [
+      "test/list_ops_test.clj"
+    ],
+    "example": [
+      ".meta/src/example.clj"
+    ]
+  },
+  "blurb": "Implement basic list operations."
+}
diff --git a/exercises/practice/list-ops/.meta/src/example.clj b/exercises/practice/list-ops/.meta/src/example.clj
new file mode 100644
index 0000000..c4bc083
--- /dev/null
+++ b/exercises/practice/list-ops/.meta/src/example.clj
@@ -0,0 +1,34 @@
+(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))
diff --git a/exercises/practice/list-ops/deps.edn b/exercises/practice/list-ops/deps.edn
new file mode 100644
index 0000000..561c3e2
--- /dev/null
+++ b/exercises/practice/list-ops/deps.edn
@@ -0,0 +1,6 @@
+{:aliases {:test {:extra-paths ["test"]
+                 :extra-deps {io.github.cognitect-labs/test-runner 
+                              {:git/url "https://github.com/cognitect-labs/test-runner.git"
+                               :sha "705ad25bbf0228b1c38d0244a36001c2987d7337"}}
+                 :main-opts ["-m" "cognitect.test-runner"]
+                 :exec-fn cognitect.test-runner.api/test}}}
\ No newline at end of file
diff --git a/exercises/practice/list-ops/project.clj b/exercises/practice/list-ops/project.clj
new file mode 100644
index 0000000..31eb1b1
--- /dev/null
+++ b/exercises/practice/list-ops/project.clj
@@ -0,0 +1,4 @@
+(defproject list-ops "0.1.0-SNAPSHOT"
+  :description "list-ops exercise."
+  :url "https://github.com/exercism/clojure/tree/main/exercises/list-ops"
+  :dependencies [[org.clojure/clojure "1.10.0"]])
diff --git a/exercises/practice/list-ops/src/list_ops.clj b/exercises/practice/list-ops/src/list_ops.clj
new file mode 100644
index 0000000..28d30ea
--- /dev/null
+++ b/exercises/practice/list-ops/src/list_ops.clj
@@ -0,0 +1,49 @@
+(ns list-ops)
+
+(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
+)
+
+(defn concatenate 
+  "Given a vector of vectors, it combines all the vectors into one flattened vector"
+  [colls]
+  ;; your code goes here
+)
+
+(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
+)
+
+(defn length 
+  "Given a vector, it returns the number of items within it"
+  [coll]
+  ;; your code goes here
+)
+
+(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
+)
+
+(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
+)
+
+(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
+)
+
+(defn reverse-order 
+  "Given a vector, it returns a vector with all the original items, but in reverse order"
+  [coll]
+  ;; your code goes here
+)


Test patch:
diff --git a/exercises/practice/list-ops/.meta/tests.toml b/exercises/practice/list-ops/.meta/tests.toml
new file mode 100644
index 0000000..08b1edc
--- /dev/null
+++ b/exercises/practice/list-ops/.meta/tests.toml
@@ -0,0 +1,106 @@
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
+
+[485b9452-bf94-40f7-a3db-c3cf4850066a]
+description = "append entries to a list and return the new list -> empty lists"
+
+[2c894696-b609-4569-b149-8672134d340a]
+description = "append entries to a list and return the new list -> list to empty list"
+
+[e842efed-3bf6-4295-b371-4d67a4fdf19c]
+description = "append entries to a list and return the new list -> empty list to list"
+
+[71dcf5eb-73ae-4a0e-b744-a52ee387922f]
+description = "append entries to a list and return the new list -> non-empty lists"
+
+[28444355-201b-4af2-a2f6-5550227bde21]
+description = "concatenate a list of lists -> empty list"
+
+[331451c1-9573-42a1-9869-2d06e3b389a9]
+description = "concatenate a list of lists -> list of lists"
+
+[d6ecd72c-197f-40c3-89a4-aa1f45827e09]
+description = "concatenate a list of lists -> list of nested lists"
+
+[0524fba8-3e0f-4531-ad2b-f7a43da86a16]
+description = "filter list returning only values that satisfy the filter function -> empty list"
+
+[88494bd5-f520-4edb-8631-88e415b62d24]
+description = "filter list returning only values that satisfy the filter function -> non-empty list"
+
+[1cf0b92d-8d96-41d5-9c21-7b3c37cb6aad]
+description = "returns the length of a list -> empty list"
+
+[d7b8d2d9-2d16-44c4-9a19-6e5f237cb71e]
+description = "returns the length of a list -> non-empty list"
+
+[c0bc8962-30e2-4bec-9ae4-668b8ecd75aa]
+description = "return a list of elements whose values equal the list value transformed by the mapping function -> empty list"
+
+[11e71a95-e78b-4909-b8e4-60cdcaec0e91]
+description = "return a list of elements whose values equal the list value transformed by the mapping function -> non-empty list"
+
+[613b20b7-1873-4070-a3a6-70ae5f50d7cc]
+description = "folds (reduces) the given list from the left with a function -> empty list"
+include = false
+
+[e56df3eb-9405-416a-b13a-aabb4c3b5194]
+description = "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list"
+include = false
+
+[d2cf5644-aee1-4dfc-9b88-06896676fe27]
+description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list"
+include = false
+
+[36549237-f765-4a4c-bfd9-5d3a8f7b07d2]
+description = "folds (reduces) the given list from the left with a function -> empty list"
+reimplements = "613b20b7-1873-4070-a3a6-70ae5f50d7cc"
+
+[7a626a3c-03ec-42bc-9840-53f280e13067]
+description = "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list"
+reimplements = "e56df3eb-9405-416a-b13a-aabb4c3b5194"
+
+[d7fcad99-e88e-40e1-a539-4c519681f390]
+description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list"
+reimplements = "d2cf5644-aee1-4dfc-9b88-06896676fe27"
+
+[aeb576b9-118e-4a57-a451-db49fac20fdc]
+description = "folds (reduces) the given list from the right with a function -> empty list"
+include = false
+
+[c4b64e58-313e-4c47-9c68-7764964efb8e]
+description = "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list"
+include = false
+
+[be396a53-c074-4db3-8dd6-f7ed003cce7c]
+description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list"
+include = false
+
+[17214edb-20ba-42fc-bda8-000a5ab525b0]
+description = "folds (reduces) the given list from the right with a function -> empty list"
+reimplements = "aeb576b9-118e-4a57-a451-db49fac20fdc"
+
+[e1c64db7-9253-4a3d-a7c4-5273b9e2a1bd]
+description = "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list"
+reimplements = "c4b64e58-313e-4c47-9c68-7764964efb8e"
+
+[8066003b-f2ff-437e-9103-66e6df474844]
+description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list"
+reimplements = "be396a53-c074-4db3-8dd6-f7ed003cce7c"
+
+[94231515-050e-4841-943d-d4488ab4ee30]
+description = "reverse the elements of the list -> empty list"
+
+[fcc03d1e-42e0-4712-b689-d54ad761f360]
+description = "reverse the elements of the list -> non-empty list"
+
+[40872990-b5b8-4cb8-9085-d91fc0d05d26]
+description = "reverse the elements of the list -> list of lists is not flattened"
diff --git a/exercises/practice/list-ops/test/list_ops_test.clj b/exercises/practice/list-ops/test/list_ops_test.clj
new file mode 100644
index 0000000..2b26907
--- /dev/null
+++ b/exercises/practice/list-ops/test/list_ops_test.clj
@@ -0,0 +1,63 @@
+(ns list-ops-test
+  (: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]])))))