Clojure - 一个函数的两个版本。哪个更符合惯用语?

3
这两个函数中,哪一个更符合惯用的方式?它们都不能代表一种可以被认为是良好的Clojure吗?有没有更优雅的方法来完成这个任务?寻求对样式/方向的建设性批评。
(在github上:https://github.com/jtrim/clojure-sandbox/blob/master/bit-sandbox/src/bit_sandbox/core.clj#L25
这两个函数的版本都接受一个表示字节的数字向量,并将字节转换为数字。例如:
(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: 循环/递归

在每个递归层级中,所涉及的字节会左移一个与该递归层级的字节数相对应的数字,然后加到运行总和中,最后返回或传递到另一个层级。

(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使用一个累加器[sum position]来减少字节向量,其中:

  • sum:移位字节的运行总和
  • position:向量中当前字节的从右到左的零基索引(注意:不是从左到右)。用于确定要将问题字节左移多少位。

:

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

以下是 shifted-byte 函数的源代码,供完整性参考:

(defn shifted-byte [num-byte-value, shift-by]
  (bit-shift-left
    (bit-and num-byte-value 0xFF)
    (* shift-by 8)))
1个回答

7

您想将字节转换为无符号大整数,是吗?

那么您可能需要类似以下的代码:

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

总体评价:

  • 通常情况下,使用Reduce比循环/递归更好且更习惯
  • 如果可以避免,您真的不想要一直维护一个位置索引

或者您可以使用Java互操作性直接使用接受字节数组的BigInteger构造函数。这里唯一需要注意的是Java需要有符号字节,因此您需要进行一些转换:

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

(BigInteger. (byte-array (map to-signed-byte [ 0 0 0 255])))
=> 255

非常好。更简单易读了许多。感谢您的反馈! - jtrim
to-signed-byte 也可以使用 Byte/valueOf(defn t-s-b [x] (Byte/valueOf (byte x)))。它为您执行转换。 - kotarak
@kotarak - 不确定这是否有效,因为(byte x)在127以上的任何值都会失败.....就我所知,没有一种很好的解决方案可以在整数类型之间进行未检查的强制转换。 - mikera
实际上不用管它。(.byteValue x) 确实可以工作......已编辑答案以使用此方法。 - mikera
啊,糟糕。抱歉,我实际上是指“byteValue”。 - kotarak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接