Могу ли я использовать фолд (или другой вид редукции) для этого кода? - PullRequest
2 голосов
/ 27 июня 2019

Я пытаюсь разжечь мои знания F #.Для практики я построил реализацию вычисления хеша FNV1a32 строки в F #.

Вот код, который я придумал:

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

И он работает правильно, и я в порядке с этим.У меня такой вопрос: я думаю, я мог бы упростить реализацию transformString, если бы мог использовать сгиб или какой-то другой вид уменьшения, но я не могу понять это.Может кто-нибудь помочь мне с реализацией transformString, которая использует сгиб или уменьшение какого-то рода?Или это так хорошо, как я могу получить?

1 Ответ

4 голосов
/ 27 июня 2019

Вы, конечно, можете, и вот как это выглядит:

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")]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...