问题
I can't seem to figure how to write a correct uscheme (A derivative of MIT Scheme) function that will return boolean whether a list contains a smaller list.
I wrote this.
(define sublist? (xs ys)
(if (= xs '())
#t
(if (= ys '())
#f
(if (= (car xs) (car ys))
(sublist? (cdr xs) (cdr ys))
(sublist? xs (cdr ys))
)
)
)
)
It passes most of my test cases except this one test case.
(sublist? '(a b c) '(1 2 a 3 b 4 c 5 6))
;; this returns true while it's supposed to return false
Test case requires that sublist is consecutive with no random element in between.
I'm a bit stuck on how to correct this. Does anyone else have an idea?
回答1:
The problem is that your algorithm is too eager—once it finds two elements that are equal, it immediately throws away that element and keeps checking. In actuality, finding two equal elements is not enough, since your algorithm might have to backtrack if it finds an incomplete match.
The easiest way to represent this sort of algorithm would be with a helper function that determines if a sublist matches at a given position in the list. The sublist?
function would then iterate through each position in the larger list looking for matches.
Here's my implementation:
(define (sublist? xs ys)
(define (sublist-equal? xs ys)
(cond
((null? xs) #t)
((null? ys) #f)
((equal? (car xs) (car ys))
(sublist-equal? (cdr xs) (cdr ys)))
(else #f)))
(cond
((null? xs) #t)
((null? ys) #f)
((sublist-equal? xs ys) #t)
(else (sublist? xs (cdr ys)))))
Note the internal sublist-equal?
helper function.
I also use cond
instead of nested if
expressions, since cond
should really be used in this case to represent this sort of logic. Furthermore, I use equal?
instead of =
, since it most Schemes I know of, =
is for numeric comparison (yours might be different, I don't know).
回答2:
Here's my version "without a helper function". It does use two nested loops, though:
(define (sublist? needle haystack)
(if (null? needle)
#t
(let ((first (car needle)))
(let outer ((cur haystack))
(define (next)
(outer (cdr cur)))
(cond ((null? cur) #f)
((eqv? (car cur) first)
(let inner ((lhs (cdr needle))
(rhs (cdr cur)))
(cond ((null? lhs) #t)
((null? rhs) (next))
((eqv? (car lhs) (car rhs))
(inner (cdr lhs) (cdr rhs)))
(else (next)))))
(else (next)))))))
回答3:
(define sublist? (xs ys)
(cond ((= xs '()) #t) ;;cond easier to read than
((= ys '()) #f) ;;nested if statements
((and (= (car xs) (car ys))
;;need to check for more than singleton match
(or (null? (cdr xs))
(and (not (null? (cdr ys)))
(= (cadr xs) (cadr ys))
(sublist? (cdr xs) (cdr ys)))))) ;not tail call
;;this cond clause has no second part,
;;the first will return #t or #f
;;will backtrack if #f is propagated up the stack.
(else (sublist? xs (cdr ys)))) ; tail call
来源:https://stackoverflow.com/questions/28776363/recursive-sublist-function