Quantcast
Channel: dave yarwood
Viewing all articles
Browse latest Browse all 64

4Clojure Problem #178 - Revised

$
0
0

I got some great feedback on my last post and decided to re-work my solution a bit. I think this turned out much nicer.

The main things I improved:

  • My first solution involved “predicates” (technically they may not have been true predicates, since they returned a keyword or nil, instead of true or false) for each possible hand within one big let binding, and then at the end I removed all the nil values and returned the first possible hand in the list. This was fairly readable, but there were two problems:
    1. The code could be more concise if I didn’t have to wrap each “predicate” in an if statement to either return the keyword or nil. Using when instead would help to some extent (since it returns either a value or nil), but a better solution altogether came from the other problem, which was that
    2. Wrapping all of my “predicates” in a let binding forces them all to be evaluated, even if we may only need to evaluate a handful of them. Using or or cond instead allows you to take advantage of short-circuiting and stop evaluating once you reach a predicate that is met, which is better from a performance standpoint. Since the first possibility we check for (i.e. the highest ranking possible hand) is a straight flush, I decided to include the predicates flush? and straight? in my let binding and then refer to them in the main part of my code within a cond statement.
  • It turns out that my previous definition of the flush? predicate, (= 1 (count (set suits))), could be simplified to (apply = suits), thanks to = being a variable-arity function in Clojure.
  • My first solution repeated this piece of code an awful lot: (some (fn [[r num]] (>= num n)) (frequencies ranks)). This was a classic opportunity to apply the DRY principle, so I added a rank-freqs symbol to my let binding. To make it easier to use, I defined it as (sort (vals (frequencies ranks))), so that the value of rank-freqs is always[2 3] for a full house, for instance.

Without further ado, here is my revised solution for Clojure Problem #178:

(fn best-hand[card-strings](let [card-parser(fn [[sr]](let [suit({\S:spade, \H:heart, 
                                   \D:diamond, \C:club}s)rank(if (> (Character/digitr10)-1)(- (Character/digitr10)2)({\T8, \J9, 
                                     \Q10, \K11, \A12}r))]{:suitsuit, :rankrank}))cards(map card-parsercard-strings)suits(map :suitcards)ranks(map :rankcards)rank-freqs(sort (vals (frequenciesranks)))flush?(apply = suits)straight?(let [aces-high(sort ranks)aces-low(sort (replace {12-1}ranks))](or(= aces-high(take 5(iterate inc (first aces-high))))(= aces-low(take 5(iterate inc (first aces-low))))))](cond (and flush?straight?):straight-flush(= rank-freqs[14]):four-of-a-kind(= rank-freqs[23]):full-houseflush?:flushstraight?:straight(some #{3}rank-freqs):three-of-a-kind(= rank-freqs[122]):two-pair(some #{2}rank-freqs):pair:else:high-card)))

Viewing all articles
Browse latest Browse all 64

Trending Articles