Более простой способ вычисления контрольной цифры GS1 в Clojure
Я пытаюсь вычислить контрольную цифру GS1 и придумала следующий код. Алгоритм вычисления контрольной цифры:
- Обратный штрих-код
- Удалить последнюю цифру (вычисленная контрольная цифра)
- Добавьте цифры вместе с первой, третьей, пятой и т. Д. Цифрой, умноженной на 3, и четными цифрами, умноженными на 1.
- Вычтите сумму из ближайшего равного или большего кратного десяти
Звучит просто, но решение, которое я придумала, казалось немного не элегантным. Это работает, но я хочу знать, есть ли более элегантный способ написать это.
(defn abs "(abs n) is the absolute value of n" [n]
(cond
(not (number? n)) (throw (IllegalArgumentException.
"abs requires a number"))
(neg? n) (- n)
:else n))
(defn sum-seq "adds (first number times 3) with (second number)"
[coll]
(+
(* (first coll) 3)
(second coll)))
(defn sum-digit
[s]
(reduce +
(map sum-seq
(partition 2 2 '(0)
(map #(Integer/parseInt %)
(drop 2 (clojure.string/split (clojure.string/reverse s) #"")))))))
(defn mod-higher10 "Subtracts the sum from nearest equal or higher multiple of ten"
[i]
(if (zero? (rem i 10))
0
(- 10(rem i 10))))
(defn check-digit "calculates a GS1 check digit"
[s]
(mod-higher10
(sum-digit s)))
(= (check-digit "7311518182472") 2)
(= (check-digit "7311518152284") 4)
(= (check-digit "7311518225261") 1)
(= (check-digit "7311518241452") 2)
(= (check-digit "7311518034399") 9)
(= (check-digit "7311518005955") 5)
(= (check-digit "7311518263393") 3)
(= (check-digit "7311518240943") 3)
(= (check-digit "00000012345687") 7)
(= (check-digit "012345670") 0)
2 ответа
Решение
(defn check-digit
[s]
(let [digits (map #(Integer/parseInt (str %)) s)
[chk & body] (reverse digits)
sum (apply + (map * body (cycle [3 1])))
moddiff (mod (- 10 sum) 10)]
moddiff))
Эта реализация использует две идиомы clojure, о которых я знаю:
let
управлять локальной декомпозицией (и повторным использованием)map
со второй коллекцией, являющейся бесконечной ленивой последовательностью, "смежной" с проблемой.
Также список де-структур, так что было бы легко написать предикат проверки как (= moddiff chk)
,
Макросы Threading -> ->> довольно хороши в цепочке приложений
(defn to-digits [s] (map #(Integer/parseInt (str %)) s))
(defn check-digit [string]
(->> string
to-digits
reverse rest
(map * (cycle [3 1]) )
(apply +)
(- 10)
(#(mod % 10))
))