Other Clojure solutions.
(ns isbn-verifier
(:require [clojure.string :as str]))
(defn sanitize-isbn [isbn]
(let [lowered (str/lower-case isbn)
sanitized (remove #(= \- %)
(butlast lowered))]
(concat sanitized [(last lowered)])))
(defn parse-isbn [sanitized]
(map (fn [c] (if (= \x c)
10
(Character/digit c 10))) sanitized))
(defn verify-isbn [parsed-isbn]
(let [len (count parsed-isbn)
formula-result (->> (range 0 len)
(map
(fn [i] (nth parsed-isbn i) * (- len i)))
(reduce +))]
(= 0 (mod formula-result 11))))
(defn isbn? [isbn]
(let [sanitized (sanitize-isbn isbn)
valid-length? (= 10 (count sanitized))
valid-structure? (every? #(Character/isDigit %) (take 9 sanitized))
valid-check-digit? (let [check-digit (last sanitized)]
(or (= \x check-digit) (Character/isDigit check-digit)))
valid-input? (and valid-length? valid-structure? valid-check-digit?)
parsed (parse-isbn sanitized)]
(and valid-input? (verify-isbn parsed))))
(isbn? "3-598-21507-X")
Other Roc solutions.
module [isValid]
isValid : Str -> Bool
isValid = \isbn ->
sanitized =
isbn
|> Str.toUtf8
|> List.dropIf \c -> c == '-'
if List.len sanitized != 10 then
Bool.false
else
checkDigit =
sanitized
|> List.last
|> Result.map \c -> if c == 'X' then 10 else c - '0'
digits =
sanitized
|> List.dropLast 1
|> List.keepIf isDigit
|> List.map \c -> c - '0'
|> List.appendIfOk checkDigit
checkSum = List.walkWithIndex digits (Num.toU64 0) \state, digit, idx ->
multiplier = 10 - idx
state + ((Num.toU64 digit) * multiplier)
checkSum % 11 == 0
isDigit = \c -> c >= '0' && c <= '9'