I am going through Lisp Koans, it's a lot of fun! But I stuck at Scoring Projects (I had a bad solution). In this project we were asked to implement a simple game called *Greed*. The problem description is here:
; *Greed* is a dice game where you roll up to five dice to accumulate
; points. The following "score" function will be used to calculate the
; score of a single roll of the dice.
;
; A greed roll is scored as follows:
; * A set of three ones is 1000 points
; * A set of three numbers (other than ones) is worth 100 times the
; number. (e.g. three fives is 500 points).
; * A one (that is not part of a set of three) is worth 100 points.
; * A five (that is not part of a set of three) is worth 50 points.
; * Everything else is worth 0 points.
;
; Examples:
;
; (score '(1 1 1 5 1)) => 1150 points
; (score '(2 3 4 6 2)) => 0 points
; (score '(3 4 5 3 3)) => 350 points
; (score '(1 5 1 2 4)) => 250 points
;
; More scoring examples are given in the tests below:
;
; Your goal is to write the score method.
My Solution is following:
WARNING! IF YOU HAVEN'T PLAY WITH THIS ONE. DO NOT SEE THIS!
I use an occurs function to calculate occurrences of number and represent in assoc-list. And a formula-wrapper function to provide correct arguments to formula function. The formula function to calculate scores. My solution is very ugly! Any advices are welcome! Thank you in advance.
(defun occurs (lst)
(let ((acc nil))
(dolist (obj lst)
(let ((p (assoc obj acc)))
(if p
(incf (cdr p))
(push (cons obj 1) acc))))
(sort acc #'> :key #'cdr)))
(defun formula-wrapper (lst)
(formula (car lst) (cdr lst)))
(defun formula (number times)
(cond ((= times 0) 0)
((= times 1)
(case number
(1 100)
(5 50)
(otherwise 0)))
((= times 2)
(case number
(1 200)
(5 100)
(otherwise 0)))
((= times 3)
(case number
(1 1000)
(otherwise (* 100 number))))
((= times 4)
(case number
(1 1100)
(5 550)
(otherwise 0)))
((= times 5)
(case number
(1 1200)
(5 600)
(otherwise 0)))
(times 0)))
(defun score (dice)
(let ((rolls (occurs dice)))
(if (null rolls)
0
(apply #'+ (mapcar #'formula-wrapper rolls))))))
The tests:
(define-test test-score-of-an-empty-list-is-zero
(assert-equal 0 (score nil)))
(define-test test-score-of-a-single-roll-of-5-is-50
(assert-equal 50 (score '(5))))
(define-test test-score-of-a-single-roll-of-1-is-100
(assert-equal 100 (score '(1))))
(define-test test-score-of-multiple-1s-and-5s-is-the-sum-of-individual-scores
(assert-equal 300 (score '(1 5 5 1))))
(define-test test-score-of-single-2s-3s-4s-and-6s-are-zero
(assert-equal 0 (score '(2 3 4 6))))
(define-test test-score-of-a-triple-1-is-1000
(assert-equal 1000 (score '(1 1 1))))
(define-test test-score-of-other-triples-is-100x
(assert-equal 200 (score '(2 2 2)))
(assert-equal 300 (score '(3 3 3)))
(assert-equal 400 (score '(4 4 4)))
(assert-equal 500 (score '(5 5 5)))
(assert-equal 600 (score '(6 6 6))))
(define-test test-score-of-mixed-is-sum
(assert-equal 250 (score '(2 5 2 2 3)))
(assert-equal 550 (score '(5 5 5 5))))
(defun score (dice)
(let ((freq (make-hash-table)))
(loop for x in dice do (incf (gethash x freq 0)))
(loop for x being the hash-key of freq using (hash-value c)
sum (if (<= 3 c)
(case x
(1 (+ 1000 (* 100 (- c 3))))
(5 (+ 500 (* 50 (- c 3))))
(t (* x 100)))
(case x
(1 (* c 100))
(5 (* c 50))
(t 0))))))
One way to write it:
(defun find-set (roll)
"which number from 1 to 6 occurs at least three times in a list of five?"
(assert (= (length roll) 5))
(loop for i from 1 to 6
when (>= (count i roll) 3)
do (return i)))
(defun score-set (i)
"compute the set score for number i"
(case i
(1 1000)
(otherwise (* i 100))))
(defun score (roll &aux (s (find-set roll)) (score 0))
(when s
(setf score (score-set s)
roll (remove s roll :count 3)))
(incf score (* (count 1 roll) 100))
(incf score (* (count 5 roll) 50))
score)
(defun test ()
(assert (= (score '(1 1 1 5 1)) 1150))
(assert (= (score '(2 3 4 6 2)) 0))
(assert (= (score '(3 4 5 3 3)) 350))
(assert (= (score '(1 5 1 2 4)) 250))
t)
A tail-recursive version:
(defun score (dice)
(labels ((iter (left ans)
(if (not left) ans
(cond ((and (>= (length left) 3)
(= (car left) (cadr left) (caddr left)))
(cond ((= (car left) 1)
(iter (cdddr left) (+ ans 1000)))
((= (car left) 5)
(iter (cdddr left) (+ ans 500)))
(t (iter (cdddr left) (+ ans (* (car left) 100))))))
((= (car left) 1) (iter (cdr left) (+ ans 100)))
((= (car left) 5) (iter (cdr left) (+ ans 50)))
(t (iter (cdr left) ans))))))
(iter (sort dice #'<) 0)))
Related
(defun modsum2 (n)
(let ((summ 0))
(if (>= n 3)
(if (or (zerop (mod n 3)) (zerop (mod n 5)))
(progn (setq summ (+ n summ))
(modsum2 (1- n)))
(modsum2 (1- n)))
(print summ))))
I am trying to get the sum of multiples of 3 and 5 below the given number. But the code always returns to 0. What is the problem with it?
(defun modsum2 (n)
(let ((summ 0))
(if (>= n 3)
(if (or (zerop (mod n 3)) (zerop (mod n 5)))
(progn (setq summ (+ n summ))
(modsum2 (1- n)))
(modsum2 (1- n)))
(print summ))))
Right, now you got it indented. Let's trace it:
* (trace modsum2)
(MODSUM2)
* (modsum2 4)
0: (MODSUM2 4)
1: (MODSUM2 3)
2: (MODSUM2 2)
0 2: MODSUM2 returned 0
1: MODSUM2 returned 0
0: MODSUM2 returned 0
0
You can see that 0 gets printed when the argument to n is 2. Since the print form is also the last form, the function returns its value. (print 0) returns 0. Since the return value is in your function used, it just gets returned from each recursive call.
A typical way to repair it would be to have a local recursive function using labels inside the let. You then need to call the function. Later you would need to return the summ.
;; your function has some flaws
(defun modsum2 (n)
(let ((summ 0)) ;; in every call, `summ` is put to `0`!
(if (>= n 3) ;; for n = 2, the alternative `(print summ)` is executed
(if (or (zerop (mod n 3)) (zerop (mod n 5)))
(progn (setq summ (+ n summ))
(modsum2 (1- n)))
(modsum2 (1- n)))
(print summ)))) ;; for n = 2 already this is called
;; since summ is set to `0` for this last modsum2 call, it prints 0
;; tail call recursion with inner function
(defun modsum2 (n)
(let ((summ 0))
(labels ((.modsum2 (.n)
(cond ((zerop .n) summ)
((or (zerop (mod .n 3)) (zerop (mod .n 5)))
(setq summ (+ .n summ))
(.modsum2 (1- .n)))
(t (.modsum2 (1- .n))))))
(print (.modsum2 n)))))
;; tail call recursion with optional accumulator for the proper start
(defun modsum2 (n &optional (acc 0))
(cond ((zerop n) acc)
((or (zerop (mod n 3))
(zerop (mod n 5)))
(modsum2 (1- n) (+ acc n)))
(t (modsum2 (1- n) acc))))
;; using loop
(defun modsum2 (n)
(loop for x from 1 to n
when (or (zerop (mod x 3)) (zerop (mod x 5)))
sum x into res
finally (return res)))
;; which is equivalent to (thanks #Rainer Joswig):
(defun modsum2 (n)
(loop for x from 1 to n
when (or (zerop (mod x 3)) (zerop (mod x 5)))
sum x))
;; using reduce or apply
(defun modsum2 (n)
(reduce #'+ (remove-if-not #'(lambda (x) (or (zerop (mod x 3))
(zerop (mod x 5))))
(loop for x from 1 to n))))
;; instead of `reduce`, `apply` would work, too.
You’re doing far too much work. Just do inclusion-exclusion:
(defun modsum2 (max)
(let ((a (floor max 3))
(b (floor max 5))
(c (floor max 15)))
(/ (- (+ (* 3 a (1+ a))
(* 5 b (1+ b)))
(* 15 c (1+ c)))
2)))
To extend this a bit to more than just 3,5:
(defun multsum (k max)
"The sum of multiples of `k' below `max'"
(let ((a (floor max k)))
(* k a (1+ a))))
(defun subsequences-reduce (f items)
(unless items (return ()))
(loop for (item . rest) on items
collect (cons 1 item)
nconc (loop for (len . val) in (subsequences-reduce f rest)
collect (cons (1+ len) (funcall f item val)))))
(defun modsum (max &rest nums)
(loop for (len . lcm) in (subsequences-reduce #'lcm nums)
sum (* (if (oddp len) 1 -1) (multsum lcm max))))
(defun modsum2 (max) (modsum max 3 5))
I have solved the same problem last week for project euler. I have noticed the way I wrote it does not included in answers. Dropping it here, it might be useful.
;;finds the multiple of 3's and 5's below the number n
;;since "or" turns t, whenever one of its arguments returns t. No need to substract multiple of 15.
(defun modsum2 (n)
(cond ((< n 3) 0)
(t (do ((i 3 (1+ i))
(summ 0))
((> i n) summ)
(cond ((or (zerop (mod i 3))
(zerop (mod i 5)))
(setq summ (+ summ i))))))))
I'm starting to get to grips with Lisp and I'm trying to write a procedure to approximate pi using the Leibniz formula at the moment; I think I'm close but I'm not sure how to proceed. The current behavior is that it makes the first calculation correctly but then the program terminates and displays the number '1'. I'm unsure if I can call a defined function recursively like this,
;;; R5RS
(define (pi-get n)
(pi 0 1 n 0))
(define (pi sum a n count)
;;; if n == 0, 0
(if (= n 0) 0)
;;; if count % 2 == 1, + ... else -, if count == n, sum
(cond ((< count n)
(cond ((= (modulo count 2) 1)
(pi (+ sum (pi-calc (+ 2 a))) (+ a 2) n (+ count 1)))
(pi
(- sum (pi-calc (+ 2 a))) (+ a 2) n (+ count 1))))))
(define (pi-calc a)
(/ 1.0 a))
Apologies if this is a little unreadable, I'm just learning Lisp a few weeks now and I'm not sure what normal formatting would be for the language. I've added a few comments to hopefully help.
As Sylwester mentioned it turned out to be a mistake on my part with syntax.
;;; R5RS
(define (pi-get n)
(pi 1 1 n 0))
(define (pi sum a n count)
(if (= n 0) 0)
(cond ((< count n)
(cond ((= (modulo count 2) 1)
(pi (+ sum (pi-calc (+ 2 a))) (+ a 2) n (+ count 1)))
((= (modulo count 2) 0)
(pi (- sum (pi-calc (+ 2 a))) (+ a 2) n (+ count 1))))
(display (* 4 sum)) (newline))))
(define (pi-calc a)
(/ 1.0 a))
I have found a problem that it says it should be solved by using recursion. The question is that given a certain number it should count the number of 8s that are present in it, but if two 8s are one next to another it should be counted as double. For example:
48 should return 1
4881 should return 4
8818 should return 5
I have made the following program in Scheme:
(define (count n)
(if (= n 0)
0
(begin
(if (= (remainder n 100) 88)
2
(begin
(if (= (remainder n 10) 8)
1
0))
)
(+ (count (quotient n 10))))))
The problem is that everytime I run it returns 0, what am I missing? I do not want to use lists or set! for using an auxiliar variable. Any help?
You have to keep iterating whenever you find a match, and the sums don't seem right. Also, instead of nesting ifs it's better to use cond, like this:
(define (count n)
(cond ((= n 0) 0)
((= (remainder n 100) 88)
(+ 4 (count (quotient n 100))))
((= (remainder n 10) 8)
(+ 1 (count (quotient n 10))))
(else
(+ (count (quotient n 10))))))
It works with your examples:
(count 48)
=> 1
(count 4881)
=> 4
(count 8818)
=> 5
It would be better to count scans of 8s in a helper and keep a current number of hits and a total tally for previous scans.
(define (funny-eights n)
(define (aux n cur total)
(cond ((= (remainder n 10) 8)
(aux (quotient n 10) (+ cur 1) total))
((> cur 1)
(aux (quotient n 10) 0 (+ total (* 2 cur))))
((= cur 1)
(aux (quotient n 10) 0 (+ total cur)))
((> n 0)
(aux (quotient n 10) 0 total))
(else
total)))
(aux n 0 0))
(funny-eights 488838288) ; ==> 11 or 3*2 + 1 + 2*2
I want to implement Heap's algorithm in Scheme (Gambit).
I read his paper and checked out lots of resources but I haven't found many functional language implementations.
I would like to at least get the number of possible permutations.
The next step would be to actually print out all possible permutations.
Here is what I have so far:
3 (define (heap lst n)
4 (if (= n 1)
5 0
6 (let ((i 1) (temp 0))
7 (if (< i n)
8 (begin
9 (heap lst (- n 1))
10 (cond
11 ; if even: 1 to n -1 consecutively cell selected
12 ((= 0 (modulo n 2))
13 ;(cons (car lst) (heap (cdr lst) (length (cdr lst)))))
14 (+ 1 (heap (cdr lst) (length (cdr lst)))))
15
16 ; if odd: first cell selectd
17 ((= 1 (modulo n 2))
18 ;(cons (car lst) (heap (cdr lst) (length (cdr lst)))))
19 (+ 1 (heap (car lst) 1)))
20 )
21 )
22 0
23 )
24 )
25 )
26 )
27
28 (define myLst '(a b c))
29
30 (display (heap myLst (length myLst)))
31 (newline)
I'm sure this is way off but it's as close as I could get.
Any help would be great, thanks.
Here's a 1-to-1 transcription of the algorithm described on the Wikipedia page. Since the algorithm makes heavy use of indexing I've used a vector as a data structure rather than a list:
(define (generate n A)
(cond
((= n 1) (display A)
(newline))
(else (let loop ((i 0))
(generate (- n 1) A)
(if (even? n)
(swap A i (- n 1))
(swap A 0 (- n 1)))
(if (< i (- n 2))
(loop (+ i 1))
(generate (- n 1) A))))))
and the swap helper procedure:
(define (swap A i1 i2)
(let ((tmp (vector-ref A i1)))
(vector-set! A i1 (vector-ref A i2))
(vector-set! A i2 tmp)))
Testing:
Gambit v4.8.4
> (generate 3 (vector 'a 'b 'c))
#(a b c)
#(b a c)
#(c a b)
#(a c b)
#(b c a)
#(c b a)
I wrote a program, that given two numbers that specify a range, should return the number (count) of numbers in that range that represented in octal form consist of a number of identical digits. For example 72->111 meets this criteria, because all the digits are the same. Examples of output:
(hw11 1 8) -> 7,(hw11 1 9) -> 8,(hw11 1 18) -> 9,(hw11 1 65) -> 14, and so on...
My problem is that to be correct my program must define only 2 procedures, and at the moment I have much more than that and have no idea how to make them less. So any help with rewriting the code is welcomed :). The code is below:
(define (count-digits n)
(if (<= n 0)
0
(+ 1 (count-digits (quotient n 10)))))
(define (toOct n)
(define (helper n octNumber i)
(if(<= n 0)
octNumber
(helper (quotient n 8)
(+ octNumber
(* (expt 10 i)
(remainder n 8)))
(+ i 1))))
(helper n 0 0))
(define (samedigits n)
(define (helper n i)
(if (<= n 0)
#t
(if (not (remainder n 10) i))
#f
(helper (quotient n 10) i))))
(helper n (remainder n 10))
)
(define (hw11 a b)
(define (helper a x count)
(if (> a x)
count
(if (samedigits (toOct x))
(helper a (- x 1) (+ count 1))
(helper a (- x 1) count))))
(helper a b 0))
You probably have restrictions and you didn't state which Scheme implementation you're using; the following is an example that has been tested on Racket and Guile:
(define (hw11 a b)
(define (iter i count)
(if (<= i b)
(let* ((octal (string->list (number->string i 8)))
(allc1 (make-list (length octal) (car octal))))
(iter (+ i 1) (if (equal? octal allc1) (+ count 1) count)))
count))
(iter a 0))
Testing:
> (hw11 1 8)
7
> (hw11 1 9)
8
> (hw11 1 18)
9
> (hw11 1 65)
14