Re: Advice for a new lisper
- From: Pascal Bourguignon <pjb@xxxxxxxxxxxxxxxxx>
- Date: Sat, 20 Jan 2007 23:08:06 +0100
benjamin.zimmerman@xxxxxxxxx writes:
Recently I've been doing some problems from http://projecteuler.net/ to
teach myself lisp. This is my first experience with lisp, bottom up
programming, and functional programming. I'm curious if I have the
right idea with my solution to this problem
http://projecteuler.net/index.php?section=problems&id=54. I got the
right answer, but I'm curious how close my lisp style is. I'd
appreciate any feedback.
-Ben Zimmerman
I run find-player1-wins with the path of poker.txt to get the answer.
(defun simplify-card (card)
"returns a list containing a number representing the card and a
letter
representing the suit"
(let ((value (subseq card 0 1)) (suit (subseq card 1 2)))
(cond
((equal "T" value) (list 10 suit))
((equal "J" value) (list 11 suit))
((equal "Q" value) (list 12 suit))
((equal "K" value) (list 13 suit))
((equal "A" value) (list 14 suit))
(t (list (parse-integer value) suit)))))
You need to abstract away!
Don't use subseq. Don't assume a card is a sequence!
(let ((value (card-value card))
(suit (card-suit card)))
)
Wouldn't it be simplier to keep the value of the card in numerical form?
You can convert between the numbers and the names when doing I/O.
Then you wouldn't need this simplify-card...
(defmethod render-card ((card card) (display stream))
(let ((value (card-value card))
(suit (card-suit card)))
(format display "~A~A"
(aref "--23456789TJQKA" value)
(cdr (assoc suit '((s . ♠) (c . ♣) (h . ♥) (d . ♦)))))))
(render-card (make-card :suit 't :value 11) *standard-output*)
J♣
(defun simplify-hand (hand)
"returns a list of all cards in simplify-card format"
(if (= 0 (length hand))
nil
(cons (simplify-card
(string-trim " " (subseq hand 0 3)))
(simplify-hand (subseq (string-trim " " hand) 2)))))
(= 0 (length list))
can be written: (zerop (length list))
or: (null list)
the later is O(1), the formers are O(length(list)).
(if cond nil something)
can be written: (unless cond something)
with the added advantage that unless takes a body, while if takes only
one expression in each branch.
Same comment, what about keeping the hands in lists instead of strings?
Unless you're processing words, strings are probably better reserved to I/O.
(defun sort-hand (hand)
(sort (simplify-hand hand) (lambda (card1 card2) (< (car card1)
(car card2)))))
(defun card-lessp (a b) ...)
(sort hand (function card-lessp))
(defun straightp (hand)
(if (null (cdr hand))
t
(and (= (caar hand) (- (caadr hand) 1)) (straightp (cdr hand)))))
Here is my implementation of straightp (I used a rule that accepts the
value of ace as either 1 or 14 in straights):
(defun straightp (h)
(flet ((consecutivep (h)
(apply (function =) 1
(mapcar (lambda (a b) (mod (- (card-weight a) (card-weight b))
*nnums*)) (rest h) h))))
(or (consecutivep h) (consecutivep (cons (car (last h)) (butlast h))))))
(defun ace-first (hand)
"puts the fifth card in the hand first and subtracts 12 from its
value.
used for straights where the A counts as a 1"
(cons (list (- (car (fifth hand)) 12) (cadr (fifth hand)))
(cons (first hand)
(cons (second hand)
(cons (third hand)
(cons (fourth hand) nil))))))
(cons a (cons b ... (cons z nil)))
is better written (list a b ... z)
(defun straight (hand)
"returns the number of the high card if the hand is a straight else
nil"
(cond
((straightp hand)
(list 5 (car (fifth hand))))
((straightp (ace-first hand))
(list 5 (car (fourth hand))))))
(defun flushp (hand)
(if (null (cdr hand))
t
(and (equal (cadar hand) (cadadr hand)) (flushp (cdr hand)))))
[...]
(defun rank-hand (hand)
(let ((hand (sort-hand hand)))
(cond
((straight-flush hand))
((four-of-a-kind hand))
((full-house hand))
((flush hand))
((straight hand))
((three-of-a-kind hand))
((two-pair hand))
((pair hand))
((high-card hand)))))
This is a lot of code. What about if we could just compute a score of
a hand, and compare the scores?
Here is what I have, unfinished and to be refactored (we are doing
almost the same thing in each clause, and I've been computing the
offsets in my head, this is a job for a macro):
(defun score (h)
(let* ((hc (histogram h :key (function card-weight))) (his (histogram hc)))
(cond
((and (= 10 (card-number (first h))) (straightp h) (same-color-p h)) ; rf
0)
((and (/= 10 (card-number (first h))) (straightp h) (same-color-p h)) ; sf
(+ 1 (high-card h)))
((= 1 (nth 4 (histogram (histogram h :key (function card-weight))))) ; 4oak
(+ 2 *nnums* (* *nnums* (position 4 hc)) (position 1 hc)))
((and (= 1 (nth 3 his)) (= 1 (nth 2 his))) ; full-house
(+ 3 (* (+ 2 *nnums*) *nnums*) (* *nnums* (position 3 hc)) (position 2 hc)))
((and (same-color-h h) (not (straightp h))) ; flush
(+ 4 (* (+ 3 (* 2 *nnums*)) *nnums*) (score-suite h)))
((straighp h) ; straight
(+ 5 (* (+ 3 (* 2 *nnums*)) *nnums*) (expt *nnums* 5) (score-straight h)))
((and (= 1 (nth 3 his)) (= 2 (nth 1 his))) ; three-of-a-kind
(+ 5 (* (+ 3 (* 2 *nnums*)) *nnums*) (* 2 (expt *nnums* 5))
))
((= 2 (nth 2 (histogram (histogram h :key (function card-weight))))) 7) ; tp
((and (= 1 (nth 2 his)) (= 3 (nth 1 his))) 8) ; op
(t
(+ ... (score-suite h))))))
(defun player1-winp (hands)
(let ((hand1 (rank-hand (subseq hands 0 14)))
(hand2 (rank-hand (subseq hands 15 29))))
Here again these subseq!!!
Just keep a list of hands, each hand being a list of cards.
Then it'll be easier to change the type of the hands when you'll want
to add derived data to each hand into a structure or an object.
Actually, I wouldn't mind if you kept the hands in a sequence and
still used subseq, on the condition that you'd abstract it away.
(let ((rank0 (rank-hand (nth-hand hands 0)))
(rank1 (rank-hand (nth-hand hands 1))))
...)
If you want to write:
(defconstant +hand-size+ 14)
(defun nth-hand (hands index)
(subseq (* +hand-size+ index) (* +hand-size+ (1+ index))))
no problem. As you see in the above LET, when we read the code, we're
not bothered by the details of implementation of nth-hand, and if we
want to change the implementation to have list of lists instead of a
big sequence with sub sequences, we can just changing these
abstractions.
--
__Pascal Bourguignon__ http://www.informatimago.com/
READ THIS BEFORE OPENING PACKAGE: According to certain suggested
versions of the Grand Unified Theory, the primary particles
constituting this product may decay to nothingness within the next
four hundred million years.
.
- References:
- Advice for a new lisper
- From: benjamin . zimmerman
- Advice for a new lisper
- Prev by Date: Re: Advice for a new lisper
- Next by Date: Re: Advice for a new lisper
- Previous by thread: Re: Advice for a new lisper
- Next by thread: Re: Advice for a new lisper
- Index(es):
Relevant Pages
|
|