为 cljc 项目复制 Java 的 String.hashCode() 方法

Replicating Java's String.hashCode() method for a cljc project

我有一个项目应该同时作为 clj 和 cljs 项目工作,它目前使用 Java String 的 hashCode 进行哈希处理,我无法更改它。我需要找到一种方法来为 cljs 场景实现相同的解决方案。

我有实现相同哈希过程的 javascript 所需的代码,我认为理想情况下我想将此函数用于 clj 和 cljs 场景,因为它是最快的哈希方法我知道的具体方式。

hashCode = function(stringToHash){
    let hash = 0;
    if (stringToHash.length === 0) return hash;
    let char;
    for (i = 0; i < p1.length; i++) {
        char = stringToHash.charCodeAt(i);
        hash = ((hash << 5) - hash) + char;
        hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
};

另一种选择是在纯 clojure 中重新创建哈希算法,但我认为这会导致性能下降。所以我的问题是如何在我的 cljc 项目中正确地要求这个 js 方法,或者如何在 clojure 中重新创建相同的算法。

这是我的`hashCode.js

goog.provide('hashCode');

/**
 * @param {string} stringToHash
 * @return {number}
 */
hashCode.hashCode = function(stringToHash){
    let hash = 0;
    if (stringToHash.length === 0) return hash;
    let char;
    for (i = 0; i < p1.length; i++) {
        char = stringToHash.charCodeAt(i);
        hash = ((hash << 5) - hash) + char;
        hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
};

我试过在我的 core.cljc 文件中这样要求 [hashCode :as hs] 但无济于事。

这是一个解决方案:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test)
  (:require [tupelo.core :as t]) )

(defn str->hashcode
  "Work-alike impl of Java String.hashCode() fn"
  [str-val]
  (let [char-codes (mapv int str-val)
        step-fn    (fn step-fn [hash-in char-code]
                     (let [hash-out (+ char-code
                                      (-
                                        (bit-shift-left hash-in 5)
                                        hash-in))
                           hash-out (bit-and hash-out 0xFFFFFFFF)]
                       hash-out))
        result     (reduce step-fn 0 char-codes)]
    result))

(dotest
  (spy "hello")
  (spyx (str->hashcode "hello"))
  (spyx (.hashCode "hello"))
  )

结果

-------------------------------
   Clojure 1.10.0    Java 12
-------------------------------

Testing tst.demo.core
:spy => "hello"
(str->hashcode "hello") => 99162322
(.hashCode "hello") => 99162322

永远记住Clojure/ClojureSciprt备忘单

您的 hashCode.js 是一个闭包库,因此您可以使用 :libs compiler option to access it. This is described in more detail on the Dependencies 页面。

(请注意,您的来源中有错字;p1 应替换为 stringToHash。)

这是一个使用 :libs 访问您的实现的示例:

$ clj -m cljs.main -co '{:libs ["hashCode.js"]}' -r
ClojureScript 1.10.520
cljs.user=> (require '[hashCode :as hs])
nil
cljs.user=> (hs/hashCode "hello")
99162322
cljs.user=> (hs/hashCode "abcdef")
-1424385949

(请注意,如果您将 :advanced :optimizations, since your code makes use of let, you would also need to specify :language-in 用作 :es6。)

该实现到 ClojureScript 的相当直接的转换是:

(defn string-hash [s]
   (loop [i 0 h 0]
     (if (< i (.-length s))
       (let [c (.charCodeAt s i)
             h (+ (- (bit-shift-left h 5) h) c)]
          (recur (inc i) (bit-and h h)))
       h)))

虽然 Closure Library 附带了一个字符串 hashCode 实现,它 documented 与 Java 的 相似 ,但它可能不适合您的使用,因为它不会生成带符号的哈希值:

cljs.user=> (goog.string/hashCode "hello")
99162322
cljs.user=> (goog.string/hashCode "abcdef")
2870581347