Repository: exercism/clojure
Pull request #197 (2017-11-19 02:13:34)

Implementing isbn-verifier


Patch:
diff --git a/config.json b/config.json
index 71f7317..4cb2a45 100644
--- a/config.json
+++ b/config.json
@@ -33,6 +33,14 @@
       "unlocked_by": null,
       "uuid": "04a6c1d6-6cce-4c87-a34b-23fdd9baf70d"
     },
+    {
+      "core": false,
+      "difficulty": 1,
+      "slug": "isbn-verifier",
+      "topics": null,
+      "unlocked_by": null,
+      "uuid": "e6411d18-d9b9-48fa-8ee6-0df8c13d3dee"
+    },
     {
       "core": false,
       "difficulty": 1,
diff --git a/exercises/isbn-verifier/README.md b/exercises/isbn-verifier/README.md
new file mode 100644
index 0000000..ee9c955
--- /dev/null
+++ b/exercises/isbn-verifier/README.md
@@ -0,0 +1,43 @@
+Check if a given ISBN-10 is valid.
+
+## Functionality
+
+Given an unknown string the program should check if the provided string is a valid ISBN-10.
+Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN.
+
+The program should allow for ISBN-10 without the separating dashes to be verified as well.
+
+## ISBN
+
+Let's take a random ISBN-10 number, say `3-598-21508-8` for this.
+The first digit block indicates the group where the ISBN belongs. Groups can consist of shared languages, geographic regions or countries. The leading '3' signals this ISBN is from a german speaking country.
+The following number block is to identify the publisher. Since this is a three digit publisher number there is a 5 digit title number for this book.
+The last digit in the ISBN is the check digit which is used to detect read errors.
+
+The first 9 digits in the ISBN have to be between 0 and 9.
+The check digit can additionally be an 'X' to allow 10 to be a valid check digit as well.
+
+A valid ISBN-10 is calculated with this formula `(x1 * 10 + x2 * 9 + x3 * 8 + x4 * 7 + x5 * 6 + x6 * 5 + x7 * 4 + x8 * 3 + x9 * 2 + x10 * 1) mod 11 == 0`
+So for our example ISBN this means:
+(3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 = 0
+
+Which proves that the ISBN is valid.
+
+## Caveats
+
+Converting from string to number can be tricky in certain languages.
+It's getting even trickier since the check-digit of an ISBN-10 can be 'X'.
+
+## Bonus tasks
+
+* Generate a valid ISBN-13 from the input ISBN-10 (and maybe verify it again with a derived verifier)
+
+* Generate valid ISBN, maybe even from a given starting ISBN
+
+## Source
+
+Converting a string into a number and some basic processing utilizing a relatable real world example.
+[https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation](https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation)
+
+## Submitting Incomplete Solutions
+It's possible to submit an incomplete solution so you can see how others have completed the exercise.
diff --git a/exercises/isbn-verifier/project.clj b/exercises/isbn-verifier/project.clj
new file mode 100644
index 0000000..814e1ca
--- /dev/null
+++ b/exercises/isbn-verifier/project.clj
@@ -0,0 +1,4 @@
+(defproject isbn-verifier "0.1.0-SNAPSHOT"
+  :description "isbn-verifier xercise."
+  :url "https://github.com/exercism/clojure/tree/master/exercises/isbn-verifier"
+  :dependencies [[org.clojure/clojure "1.8.0"]])
diff --git a/exercises/isbn-verifier/src/example.clj b/exercises/isbn-verifier/src/example.clj
new file mode 100644
index 0000000..fecf069
--- /dev/null
+++ b/exercises/isbn-verifier/src/example.clj
@@ -0,0 +1,17 @@
+(ns isbn-verifier)
+
+(defn is-in? [xs x] (some #(= x %) xs))
+
+(defn isbn-chars [isbn]
+  (filter #(is-in? [\0 \1 \2 \3 \4 \5 \6 \7 \8 \9 \X] %) isbn))
+
+(defn isbn? [isbn]
+  (let [chars (isbn-chars isbn)
+        nums  (map #(if (= \X %) 10 (Character/digit % 10)) chars)]
+    (and (-> chars butlast (is-in? \X) not)
+         (= 10 (count chars))
+         (as-> nums x
+               (map #(* %1 %2) (range 10 0 -1) x)
+               (reduce + x)
+               (mod x 11)
+               (zero? x)))))
diff --git a/exercises/isbn-verifier/src/isbn_verifier.clj b/exercises/isbn-verifier/src/isbn_verifier.clj
new file mode 100644
index 0000000..4e9c62d
--- /dev/null
+++ b/exercises/isbn-verifier/src/isbn_verifier.clj
@@ -0,0 +1,3 @@
+(ns isbn-verifier)
+
+(defn isbn? [isbn])


Test patch:
diff --git a/exercises/isbn-verifier/test/isbn_verifier_test.clj b/exercises/isbn-verifier/test/isbn_verifier_test.clj
new file mode 100644
index 0000000..015bc68
--- /dev/null
+++ b/exercises/isbn-verifier/test/isbn_verifier_test.clj
@@ -0,0 +1,42 @@
+(ns isbn-verifier-test
+  (:require [clojure.test :refer [deftest is]]
+            [isbn-verifier :refer [isbn?]]))
+
+(deftest valid-isbn-number
+  (is (= true (isbn? "3-598-21508-8"))))
+
+(deftest invalid-isbn-check-digit
+  (is (= false (isbn? "3-598-21508-9"))))
+
+(deftest valid-isbn-number-with-a-check-digit-of-10
+  (is (= true (isbn? "3-598-21507-X"))))
+
+(deftest check-digit-is-a-character-other-than-X
+  (is (= false (isbn? "3-598-21507-A"))))
+
+(deftest invalid-character-in-isbn
+  (is (= false (isbn? "3-598-2K507-0"))))
+
+(deftest X-is-only-valid-as-a-check-digit
+  (is (= false (isbn? "3-598-2X507-9"))))
+
+(deftest valid-isbn-without-separating-dashes
+  (is (= true (isbn? "3598215088"))))
+
+(deftest isbn-without-separating-dashes-and-X-as-check-digit
+  (is (= true (isbn? "359821507X"))))
+
+(deftest isbn-without-check-digit-and-dashes
+  (is (= false (isbn? "359821507"))))
+
+(deftest too-long-isbn-and-no-dashes
+  (is (= false (isbn? "3598215078X"))))
+
+(deftest isbn-without-check-digit
+  (is (= false (isbn? "3-598-21507"))))
+
+(deftest too-long-isbn
+  (is (= false (isbn? "3-598-21507-XX"))))
+
+(deftest check-digit-of-X-should-not-be-used-for-0
+  (is (= false (isbn? "3-598-21515-X"))))