Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure - 2 versions of 1 function. Which is more idiomatic?

Of the two functions included here, which is the more idiomatic of the two? Do neither represent something that could be considered good Clojure? Is there a much more elegant way of accomplishing this? Looking for some constructive criticisms on style / direction.

(on github: https://github.com/jtrim/clojure-sandbox/blob/master/bit-sandbox/src/bit_sandbox/core.clj#L25)

Both versions of this function take a vector of number-represented bytes and turns the bytes into a number. e.g.

(bytes-to-num [0 0 0 0])   ;=> number `0`
(bytes-to-num [0 0 0 255]) ;=> number `255`
(bytes-to-num [0 0 1 0])   ;=> number `256`
; etc...

v1: loop/recur

At each level of recursion, the byte in question is shifted left a number corresponding to the number of bytes at that given level of recursion, then added to the running sum and either returned or passed down another level.

(defn bytes-to-num-v1 [vec-bytes]
  (loop [sum 0, the-bytes vec-bytes]
    (if (empty? the-bytes)
      sum
      (recur
        (+ sum (shifted-byte (first the-bytes) (count (rest the-bytes))))
        (rest the-bytes)))))

v2: reduce

v2 reduces the vector of bytes with an accumulator of [sum position] where:

  • sum: The running sum of shifted bytes
  • position: The zero-based index (note: from the right, not the left) of the current byte in the vector. Used to determine how many bits to left-shift the byte in question.

:

(defn bytes-to-num-v2 [vec-bytes]
  (first (reduce
    (fn [acc, the-byte]
      [(+ (first acc) (shifted-byte the-byte (last acc))) (dec (last acc))])
    [0 (dec (count vec-bytes))]
    vec-bytes)))

Source of the shifted-byte function for completeness:

(defn shifted-byte [num-byte-value, shift-by]
  (bit-shift-left
    (bit-and num-byte-value 0xFF)
    (* shift-by 8)))
like image 632
jtrim Avatar asked Jan 31 '26 22:01

jtrim


1 Answers

You're trying to convert the bytes into an unsigned big integer, right?

In which case you probably want something like:

(defn bytes-to-num [bytes]
  (reduce (fn [acc x] (+ x (* 256 acc))) bytes))

General comments:

  • Reduce is usually a better and more idiomatic option than loop / recur
  • You don't really want to have to pull a position index around if you can help it

Alternatively you can use the Java interop to directly use the BigInteger constructor that accepts a byte array. The only trickiness here is that Java expects signed bytes, so you need to do a little conversion first:

(defn to-signed-byte [x] (.byteValue x))

(BigInteger. (byte-array (map to-signed-byte [ 0 0 0 255])))
=> 255
like image 109
mikera Avatar answered Feb 03 '26 02:02

mikera



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!