简体   繁体   中英

Make subdivisions of lists in Racket

I want to make a function in Scheme that makes sublists out of a list in a way that I can give a value to start the subdivision and a value to stop it, something like this:

(function '(1 2 3 1 4 5 6 3) 1 3)
>'((1 2 3) (1 4 5 6 3))

And I can't find a correct way to do it, as you can see the function would start a sublist with 1 and end it with 3, how can I implement something like this?

This is non greedy so it makes the shortest answers (stops at the very first end after it has encountered start) and continues looking for matches after the last end position. It's a roll your own solution without fancy list procedures but it's also tail recursive.

(define (sublist src start end)
  (let loop ((lst src) (acc #f) (results '()))
    (cond 
      ((null? lst) (reverse results)) ; remove reverse if the order of matches isn't an issue
      ((not acc) (loop (cdr lst) (if (eqv? (car lst) start) (list (car lst)) #f) results))
      ((not (eqv? (car lst) end)) (loop (cdr lst) (cons (car lst) acc) results))
      (else (loop (cdr lst) #f (cons (reverse (cons (car lst) acc)) results))))))


(sublist '(1 2 1 3 2 2 2 2 2) 1 2) ; ==> ((1 2) (1 3 2))
(sublist '(1 2 3 1 4 5 6 3 1 4 8 7 9 5) 4 5) ; ((4 5) (4 8 7 9 5))

For something to find overlapping results this might work:

(define (sublist-full src start end)
  (let loop ((lst src) (acc #f) (backtrack #f) (results '()))
    (if (null? lst)
        (if backtrack 
            (loop backtrack #f #f results)
            (reverse results))
        (let ((a (car lst)))
          (if acc
              (cond ((and (eqv? a start) (not backtrack)) (loop (cdr lst) (cons a acc) lst results))
                    ((eqv? a end) (loop (cdr lst) (cons a acc) backtrack (cons (reverse (cons a acc)) results)))
                    (else (loop (cdr lst) (cons a acc) backtrack results)))
              (if (eqv? a start)
                  (loop (cdr lst) (cons a '()) #f results)
                  (loop (cdr lst) #f #f results)))))))

(sublist-full '(1 2 1 3 2 2 2 2 2) 1 2) 
; ==> ((1 2) (1 2 1 3 2) (1 2 1 3 2 2) (1 2 1 3 2 2 2) 
;      (1 2 1 3 2 2 2 2) (1 2 1 3 2 2 2 2 2) (1 3 2) 
;      (1 3 2 2) (1 3 2 2 2) (1 3 2 2 2 2) (1 3 2 2 2 2 2))

Here's the best I could come up with — there's undoubtedly much more elegant ways of doing it.

The idea is to drop everything until you find the "start" value, then keep everything until the "stop" value (if there is one) and then recurse over the remaining list.

#lang racket
(require srfi/1) ; For drop-while

(define (subdiv ls start stop)
  (let ([part-one (drop-while (lambda (x) (not (= x start))) ls)])
    (let-values ([(main rest) (splitf-at part-one (lambda (x) (not (= x stop))))])
      (if (null? rest)         ; empty means the stop value wasn't found
          '()
          (cons (append main (list stop)) 
                (subdiv (cdr rest) start stop))))))

Example:

> (subdiv '(3 4 5 1 111 1 1 1 2 2 3 1 2) 1 2)
'((1 111 1 1 1 2) (1 2))
> (subdiv '(3 4 5 1 2 3 1 2) 1 2)
'((1 2) (1 2))
> (subdiv '(1 2) 1 2)
'((1 2))
> (subdiv '(1) 1 2)
'()
> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM