views:

226

answers:

2

I'm getting my feet wet with Clojure, and trying to get used to functional programming.

I've been translating various imperative functions from other languages into their Clojure equivalents -- and so far everything has been going well. However, I've now run into a sticking point and I have no idea how to translate this Java method into idiomatic Clojure.

At first "map" seemed like the right tool, but after playing with it a bit I'm not so sure. Can someone show me how to write this function in Clojure?

Thanks!

public String calculateChecksum(String str)
{
    String hash = "bjytk3lfj%3jklDskj";
    int key = 1690912;

    for(int i=0; i < str.length(); i++) {

        key = key ^ (int)(hash.charAt(i%hash.length()))^(int)(str.charAt(i));
        key = key>>>23|key<<9;

    }return "8"+toHex8(key>>>(8&255))+toHex8(key&255);

}
+1  A: 

Clojure does not expose the >>> operator, so a direct translation is not possible.

Timothy Pratley
OK, I'll admit I sloppied on that and did a simple right shift. Let's see if I can fix that up...
Carl Smotricz
Ah, excellent... because we start with a positive number and because it keeps growing and becomes a BigInt, it is never positive. So >> yields the same result as >>> . Phew!
Carl Smotricz
:) You can still create a mathematically equivalent function in Clojure that's for sure :)
Timothy Pratley
I'll leave that "as an exercise for the reader" ;)
Carl Smotricz
+6  A: 

We're just past Hallow'een, and it's... the night of the living n00bs!

I have just a few days of Clojure programming under my belt. This effort is closer to "real" Clojure, and at least it compiles. It also produces a result, but probably not the correct one. More after this:

(ns erikcw)

(defn toHex8 [n] (format "%08x" n))        ; Just a guess!

                                           ; can't use str, that's predefined.
(defn calculateChecksum [url]               ; I renamed the arg to url so I can use strn later.
  (loop [strn url                          ; this will loop over chars in strn.
         hash (cycle "bjytk3lfj%3jklDskj") ; now hash repeats for as long as you need it.
         key 1690912]                      ; modifying key along the way.
    (prn strn key)                           ; debug print.
    (let [k2 (bit-xor (bit-xor key (int (first hash))) (int (first strn)))
          k3 (bit-or (bit-shift-right k2 23) (bit-shift-left k2 9))]
      (if (empty? (rest strn))
        (str "8" (toHex8 (bit-shift-right k3 8)) (toHex8 (bit-and k3 255)))
        (recur (rest strn) (rest hash) k3)))))

(prn (calculateChecksum "HowNowBrownCow"))

I don't know what the toHex8 function does, so I wrote a function that prints its argument as an 8 digit hex number. Just to get the dang thing to compile.

Rather than use an index to pull characters out of hash and strn, I treat both as sequences of characters and deal with only their head elements in each iteration. hash is infinitely long, thanks to (cycle).

The bit operations have names starting with "bit-".

Because integers can become arbitrarily large in Clojure, the resulting number becomes bigger with every character thanks to the << 9. That's probably not intended.

Anyway, some spoilsport just posted what will probably be a correct answer. Still, this was fun, I hope I managed to share a little of the effort with you.

Edit: Because Dave Ray is insisting on using (reduce), I've done another solution:

(defn next-key [key str-hash]
  (let [str1 (first str-hash)
        hash1 (second str-hash)
        k2 (bit-xor (bit-xor key hash1) str1)]
        (bit-or (bit-shift-right k2 23) (bit-shift-left k2 9))))

(defn calculateChecksum2 [url]
  (let [kk
    (reduce next-key 1690912
      (partition 2                ; (72 98) (111 106) (119 121) ...
        (map int                  ; 72 98 111 106 119 121
          (interleave url (cycle "bjytk3lfj%3jklDskj"))))) ; "HbojwyNt..."
  ]
  (str "8" (toHex8 (bit-shift-right kk 8)) (toHex8 (bit-and kk 255)))))

(prn (calculateChecksum2 "HowNowBrownCow"))

This one is a little easier to read and needs no loop. next-key could have been dragged inside the main function but I find things easier to understand like this.

We have a list of hash values and one of string values. To make reduce work I had to crunch them up into a single list; see comments.

We still have the problem that the original algorithm wasn't intended to work with integers of unlimited size, plus a possible parenthesization problem in its last line. You may want to build your own truncating bit-twiddling functions.

Carl Smotricz
+1 for the effort, but Wow! If this really is the best we can do in Clojure I wonder if we've discovered an area (bit-twiddling) where Clojure is _less_ concise than Java. I'm not knocking the proposed solution. (I sure couldn't do better.) But the Java version reads smooth as silk compared to what we've come up with.
clartaq
Well, Clojure loses a bit on needing wordy function names where Java has concise operators. And I'm pretty sure the REAL Clojurians could have done better than I did. For one thing, I missed an opportunity for destructuring in next-key.
Carl Smotricz