我正在尝试重新激发我对F#的了解。为了练习,我构建了一个实现来计算F#中字符串的FNV1a32哈希。
这是我想出的代码:
let XorWithHash b hash =
hash ^^^ b
let MultiplyByPrimeFactor hash =
let Prime = 16777619un
hash * Prime
let GetNthByteOfString (s:string) n =
if (n < Encoding.UTF8.GetByteCount(s)) then Some(unativeint (Encoding.UTF8.GetBytes(s).[n])) else None
let GetFNV1a32 s =
let rec transformString s n (acc:unativeint)=
let b = GetNthByteOfString s n
match b with
| Some b ->
XorWithHash b acc
|> MultiplyByPrimeFactor
|> transformString s (n+1)
| None -> acc
let OffsetBasis = 2166136261un
transformString s 0 OffsetBasis
let Main =
let answer = GetFNV1a32 "Test String"
answer
它可以正常工作,我对此表示满意。我的问题是:我认为如果可以使用fold或其他某种reduce方法,我可以简化transformString
的实现,但我似乎还不太清楚。谁能帮我实现使用fold或reduce的transformString实现?还是这和我可能得到的一样好?
答案 0 :(得分:4)
您当然可以,这是这样的:
let GetFNV1a32 (s: string) =
let offsetBasis = 2166136261un
// We only have to get the bytes once; now we have an entire array that we can perform monadic operations on
Encoding.UTF8.GetBytes s
// Array.fold's signature is ('State -> 't -> 'State) -> 'State -> 't[] -> 'State
// So here 'State is unativeint, and 't is byte, which is the current item of the byte[]. We can just transform it in one go to our output value, which becomes the value of acc the next time around.
|> Array.fold (fun acc byt -> MultiplyByPrimeFactor (XorWithHash (unativeint byt) acc))
offsetBasis // initial value
在GetFNV1a32_old
为OP的情况下,这是一项快速测试以证明其有效:
let xs =
[for str in ["TestString"; "Test String"; "foo BAR"; "BÄz qúåx"] do
let old, neww = GetFNV1a32_old str, GetFNV1a32 str
yield old, neww, (sprintf "does old = neww? %b" (old = neww))]
这将导致:
val xs : (unativeint * unativeint * string) list =
[(17683775798505137816un, 17683775798505137816un, "is old = neww? true");
(3444292159790811978un, 3444292159790811978un, "is old = neww? true");
(17137498406203898314un, 17137498406203898314un, "is old = neww? true");
(13890330577974722754un, 13890330577974722754un, "is old = neww? true")]