Reputation: 495
I am replicating racket's length function using just car & cdr to compare then length of two lists and return the shorter of the two.
The length function simply put is:
(define length
(lambda (list)
(if (null? list)
0
(+ 1 (length (cdr list))))))
and when used in comparison of two lists
(define short
(lambda (list1 list2)
(if (<= (length list1) (length list2))
list1
list2)))
> (short '(a b c) '(1 2 3 4 5 6 7))
will return '(a b c).
However this method is ineffective especially when one list is much longer than the other, as it will iterate through both lists before returning the shorter.
I have a more effective method below. However I was wondering if there was a more efficient/alternate method to getting the shorter length without checking to the end of both lists. Perhaps by recursively going through the lists simultaneously with car/cdr, until the shorter list reaches its end first.
(define shorter?
(lambda (list1 list2)
(and (not (null? list2))
(or (null? list1)
(shorter? (cdr list1) (cdr list2))))))
(define shorter
(lambda (list1 list2)
(if (shorter? list2 list1)
list2
list1)))
Upvotes: 2
Views: 105
Reputation: 236004
Your shorter?
procedure is already as efficient as possible - both the and
and or
special forms short-circuit and will stop when the value of any of the evaluated expressions is true
(for the or
special form) or false
(for the and
special form). So as soon as one of the lists reaches null
, the recursion stops. Your shorter?
implementation is equivalent to, and as efficient as this:
(define shorter?
(lambda (list1 list2)
(cond ((null? list2) #f)
((null? list1) #t)
(else (shorter? (cdr list1) (cdr list2))))))
If you want a more compact solution you can put both procedures inside a single procedure, using a named let
(as demonstrated in the other answer) or an inner helper procedure, both are equivalent. I'll demonstrate the later approach:
(define (shorter lst1 lst2)
(define (shorter-list list1 list2)
(cond ((null? list2) lst2)
((null? list1) lst1)
(else (shorter-list (cdr list1) (cdr list2)))))
(shorter-list lst1 lst2))
Upvotes: 2
Reputation: 24545
The 'named let' method provides easily understable code. The comments provide the explanation:
(define (shorter l1 l2)
(let loop ((al l2) ; start with both full lists
(bl l2))
(cond
[(empty? al) l1] ; if al finishes, return l1 and end;
[(empty? bl) l2] ; if bl finishes, return l2 and end;
[else
(loop (cdr al) (cdr bl))] ; else loop again with cdr of lists;
)))
This function will end when shorer list finishes and will not needlessly continue till end of longer list.
Upvotes: 2
Reputation: 24
I recommend starting like this:
(define (shorter list-1 list-2) (shorter-loop list-1 list-2 list-1 list-2))
then
(define (shorter-loop list-1 list-2 result-1 result-2)
;;
)
where the helper function shorter-loop recurses down list-1 and list-2 simultaneously. If list-1 becomes null it returns result-1, if list-2 returns null it returns result-2.
Upvotes: -1