Hello!<br><br>An invitation:<br>How would you write the fastest possible scheme program that computes the cartesian product of a set?<br><br>Example:<br>=================<br><br>set1 = {0, 1}<br><br>then<br><br>cartesian_product = {(0, 0), (0, 1), (1, 0), (1, 1)}
<br><br><br>Note:<br>If set1 has n elements (cardinality 2), then cartesian_product will have 2^n pairs (cardinality 2^n)<br><br><br><br>In Scheme<br>~~~~~~~~~~<br><br>(define-struct onepair (left right))<br><br><br>(define set1 (build-list 2 identity)) = (list 0 1)
<br><br>(cartesian-product set1)<br>=<br>(list (make-onepair 0 0)<br> (make-onepair 0 1)<br> (make-onepair 1 0)<br> (make-onepair 1 1))<br><br><br><br><br><br>There are incredibly many ways of writing a definition which does the cartesian product!
<br>And these definitions can be reordered and rearranged and changed (local etc.) in MANY ways.<br><br>Below are a few examples.<br><br>Are there some efficiency guidelines for coding in Scheme? (Especially when considering the scheme code below:)
<br><br>Some questions in my mind are:<br>Is it better to use an unchanging parameter in a recursive function (just passing it through the whole time), or is it better to use a local?)<br>Is it better to build up a list in normal order or reverse order?
<br>Isn't the use of append, to be avoided when possible?<br><br>(weblink??)<br><br><br>Any comments or suggestions will be appreciated!<br><br>Kind regards,<br>Albert Neumüller<br>(The present author has been writing scheme code for
2.5 months and is still learning [HTDP])<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>;; Data Definition:<br><br>;; A SV is a Scheme value<br><br><br>(define-struct onepair (left right))<br>;; A onepair is a structure : (make-onepair l r), where l and r are SV (scheme-values)
<br><br><br><br>;;===================================================<br>;;cartesian-product1 : (listof SV) -> (listof onepair)<br>(define (cartesian-product1 alist)<br> (do-cartesian1 alist alist alist))<br><br>;;do-cartesian1 : (listof SV) (listof SV) (listof SV) -> (listof onepair)
<br>(define (do-cartesian1 x y alist)<br> (cond<br> [(empty? x) empty]<br> [(empty? y) (do-cartesian1 (rest x) alist alist)]<br> [else (cons (make-onepair (first x) (first y))<br> (do-cartesian1 x (rest y) alist))]))
<br>;;===================================================<br><br>;; In do-cartesian1 the 3rd parameter ( alist) does not change. Hence we can put it into a local:<br><br>;;===================================================
<br>;;cartesian-product1b : (listof SV) -> (listof onepair)<br>(define (cartesian-product1b alist)<br> (local ((define (do-cartesian1b x y)<br> (cond<br> [(empty? x) empty]<br> [(empty? y) (do-cartesian1b (rest x) alist)]
<br> [else (cons (make-onepair (first x) (first y))<br> (do-cartesian1b x (rest y)))])))<br> (do-cartesian1b alist alist)))<br>;;===================================================
<br><br>;; In do-cartesian1b, the same value (first x) is used in repeated recursions (over y). Hence we can pass it as a parameter x-single:<br><br>;;===================================================<br>;;cartesian-product1c : (listof SV) -> (listof onepair)
<br>(define (cartesian-product1c alist)<br> (local ((define (do-cartesian1c x-single x-rest y)<br> (cond<br> [(empty? y) (cond<br> [(empty? x-rest) empty]<br> [else (do-cartesian1c (first x-rest) (rest x-rest) alist)])]
<br> [else (cons (make-onepair x-single (first y))<br> (do-cartesian1c x-single x-rest (rest y)))])))<br> (cond<br> [(empty? alist) empty]<br> [else (do-cartesian1c (first alist) (rest alist) alist)])))
<br>;;===================================================<br><br>;; If order does not matter then we can use, where we end up with a reversed result:<br><br>;;===================================================<br>;;cartesian-product2 : (listof SV) -> (listof onepair)
<br>(define (cartesian-product2 alist)<br> (local ((define (do-cartesian2 x y growing-result)<br> (cond<br> [(empty? x) growing-result]<br> [else (do-cartesian2 (rest x) <br> y
<br> (cartesian-with-fixed-x2 (first x) y growing-result))]))<br> (define (cartesian-with-fixed-x2 single-x y growing-result)<br> (cond<br> [(empty? y) growing-result]
<br> [else (cons (make-onepair single-x (first y))<br> (cartesian-with-fixed-x2 single-x (rest y) growing-result))])))<br> (do-cartesian2 alist alist empty)))<br>;;===================================================
<br><br>;; In do-cartesian2 the 2nd parameter (y) is always equal to the outer paremeter alist, hence we can remove it:<br>;; (unfortunately cartesian-product2b actually seems to perform worse than cartesian-product2!! why?)
<br><br>;;===================================================<br>;;cartesian-product2b : (listof SV) -> (listof onepair)<br>(define (cartesian-product2b alist)<br> (local ((define (do-cartesian2b x growing-result)<br>
(cond<br> [(empty? x) growing-result]<br> [else (do-cartesian2b (rest x) <br> (cartesian-with-fixed-x2b (first x) alist growing-result))]))<br> (define (cartesian-with-fixed-x2b single-x y growing-result)
<br> (cond<br> [(empty? y) growing-result]<br> [else (cons (make-onepair single-x (first y))<br> (cartesian-with-fixed-x2b single-x (rest y) growing-result))])))
<br> (do-cartesian2b alist empty)))<br>;;===================================================<br><br>;; In cartesian-with-fixed-x2b, the 3rd parameter is fixed. Hence we can use a local:<br><br>;;===================================================
<br>;;cartesian-product3 : (listof SV) -> (listof onepair)<br>(define (cartesian-product3 alist)<br> (local ((define (do-cartesian3 x growing-result)<br> (cond<br> [(empty? x) growing-result]<br>
[else (do-cartesian3 (rest x) <br> (local ((define (cartesian-with-fixed-x3 single-x y)<br> (cond<br> [(empty? y) growing-result]
<br> [else (cons (make-onepair single-x (first y))<br> (cartesian-with-fixed-x3 single-x (rest y)))])))<br> (cartesian-with-fixed-x3 (first x) alist)))])))
<br> (do-cartesian3 alist empty)))<br>;;===================================================<br><br>;; In cartesian-with-fixed-x3, the 1st parameter ( single-x) doesn not change. Hence we can add it to a local expression:
<br><br>;;===================================================<br>;;cartesian-product3b : (listof SV) -> (listof onepair)<br>(define (cartesian-product3b alist)<br> (local ((define (do-cartesian3b x growing-result)<br>
(cond<br> [(empty? x) growing-result]<br> [else (do-cartesian3b (rest x) <br> (local ((define single-x (first x))<br> (define (cartesian-with-fixed-x3b y)
<br> (cond<br> [(empty? y) growing-result]<br> [else (cons (make-onepair single-x (first y))
<br> (cartesian-with-fixed-x3b (rest y)))])))<br> (cartesian-with-fixed-x3b alist)))])))<br> (do-cartesian3b alist empty)))<br>
;;===================================================<br><br><br>;; Using append<br><br>;;===================================================<br>;;cartesian-product4 : (listof SV) -> (listof onepair)<br>(define (cartesian-product4 alist)
<br> (local ((define (do-cartesian4 x)<br> (cond<br> [(empty? x) empty]<br> [else (append (do-cartesian4 (rest x))<br> (cartesian-with-fixed-x4 (first x) alist))]))
<br> (define (cartesian-with-fixed-x4 single-x y)<br> (cond <br> [(empty? y) empty]<br> [else (cons (make-onepair single-x (first y))<br> (cartesian-with-fixed-x4 single-x (rest y)))])))
<br> (do-cartesian4 alist)))<br>;;===================================================<br><br>;; Swapping arguments of append (faster):<br><br>;;===================================================<br>;;cartesian-product4b : (listof SV) -> (listof onepair)
<br>(define (cartesian-product4b alist)<br> (local ((define (do-cartesian4b x)<br> (cond<br> [(empty? x) empty]<br> [else (append (cartesian-with-fixed-x4b (first x) alist)<br> (do-cartesian4b (rest x)))]))
<br> (define (cartesian-with-fixed-x4b single-x y)<br> (cond <br> [(empty? y) empty]<br> [else (cons (make-onepair single-x (first y))<br> (cartesian-with-fixed-x4b single-x (rest y)))])))
<br> (do-cartesian4b alist)))<br>;;===================================================<br><br>;; In cartesian-with-fixed-x4b, the 1st parameter (single-x) does not change. Hence we pull it into a local:<br><br>;;===================================================
<br>;;cartesian-product4c : (listof SV) -> (listof onepair)<br>(define (cartesian-product4c alist)<br> (local ((define (do-cartesian4c x)<br> (cond<br> [(empty? x) empty]<br> [else (append (local ((define single-x (first x))
<br> (define (cartesian-with-fixed-x4c y)<br> (cond <br> [(empty? y) empty]<br> [else (cons (make-onepair single-x (first y))
<br> (cartesian-with-fixed-x4c (rest y)))])))<br> (cartesian-with-fixed-x4c alist))<br> (do-cartesian4c (rest x)))]))
<br> )<br> (do-cartesian4c alist)))<br>;;===================================================<br><br>(define mylist (build-list 2 identity))<br><br>(cartesian-product1 mylist)<br>(cartesian-product1b mylist)<br>
(cartesian-product1c mylist)<br>(cartesian-product2 mylist)<br>(cartesian-product2b mylist)<br>(cartesian-product3 mylist)<br>(cartesian-product3b mylist)<br>(cartesian-product4 mylist)<br>(cartesian-product4b mylist)<br>
(cartesian-product4c mylist)<br><br><br>;(time (cartesian-product1 mylist))<br>;(time (cartesian-product1b mylist))<br>;(time (cartesian-product1c mylist))<br>;(time (cartesian-product2 mylist))<br>;(time (cartesian-product2b mylist))
<br>;(time (cartesian-product3 mylist))<br>;(time (cartesian-product3b mylist))<br>;(time (cartesian-product4 mylist))<br>;(time (cartesian-product4b mylist))<br>;(time (cartesian-product4c mylist))<br><br><br>