More idiomatic way to calculate GS1 check digit in Clojure

只愿长相守 提交于 2020-01-02 07:32:42

问题


I am trying to calculate a GS1 check digit and have come up with the following code. The algorithm for calculating a check digit is:

  1. Reverse the barcode
  2. Drop the last digit (calculated check digit)
  3. Add the digits together with first, third, fifth, e.t.c. digit multiplied by 3 and even digits multiplied by 1.
  4. Subtract the sum from nearest equal or higher multiple of ten

It sounds simple typed out but the solution I came up with seemed a bit inelegant. It does work but I want to know if there is a more elegant way of writing this.

(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)

回答1:


(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))  

This implementation uses two clojure idioms I am conscious of:

  • let to manage local decomposition (and reuse)
  • map with a second collection, being an infinite lazy sequence 'adjacent' to the problem.

Also de-structures list so that would be easy to write the checking predicate as (= moddiff chk).




回答2:


Threading macros -> ->> are pretty great at chaining function applications

(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))
   ))


来源:https://stackoverflow.com/questions/17921814/more-idiomatic-way-to-calculate-gs1-check-digit-in-clojure

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!