How do I take a slice of a list (A sublist) in scheme?

20,460

Solution 1

The following code will do what you want:

(define get-n-items
    (lambda (lst num)
        (if (> num 0)
            (cons (car lst) (get-n-items (cdr lst) (- num 1)))
            '()))) ;'

(define slice
    (lambda (lst start count)
        (if (> start 1)
            (slice (cdr lst) (- start 1) count)
            (get-n-items lst count))))

Example:

> (define l '(2 3 4 5 6 7 8 9)) ;'
()
> l
(2 3 4 5 6 7 8 9)
> (slice l 2 4)
(3 4 5 6)
> 

Solution 2

Strangely, slice is not provided with SRFI-1 but you can make it shorter by using SRFI-1's take and drop:

(define (slice l offset n)
  (take (drop l offset) n))

I thought that one of the extensions I've used with Scheme, like the PLT Scheme library or Swindle, would have this built-in, but it doesn't seem to be the case. It's not even defined in the new R6RS libraries.

Solution 3

You can try this function:

subseq sequence start &optional end

The start parameter is your offset. The end parameter can be easily turned into the number of elements to grab by simply adding start + number-of-elements.

A small bonus is that subseq works on all sequences, this includes not only lists but also string and vectors.

Edit: It seems that not all lisp implementations have subseq, though it will do the job just fine if you have it.

Solution 4

Here's my implementation of slice that uses a proper tail call

(define (slice a b xs (ys null))
  (cond ((> a 0) (slice (- a 1) b (cdr xs) ys))
        ((> b 0) (slice a (- b 1) (cdr xs) (cons (car xs) ys)))
        (else (reverse ys))))

(slice 0 3 '(A B C D E F G)) ;=> '(A B C)
(slice 2 4 '(A B C D E F G)) ;=> '(C D E F)

Solution 5

(define (sublist list start number)
  (cond ((> start 0) (sublist (cdr list) (- start 1) number))
        ((> number 0) (cons (car list)
                      (sublist (cdr list) 0 (- number 1))))
        (else '())))
Share:
20,460
troelskn
Author by

troelskn

I write software for a living. And for fun. My CV Open source code that I made

Updated on July 09, 2022

Comments

  • troelskn
    troelskn almost 2 years

    Given a list, how would I select a new list, containing a slice of the original list (Given offset and number of elements) ?

    EDIT:

    Good suggestions so far. Isn't there something specified in one of the SRFI's? This appears to be a very fundamental thing, so I'm surprised that I need to implement it in user-land.

  • troelskn
    troelskn over 15 years
    There's a typo in get-n-items - The else-part of the if-form needs a quote. Can you edit that?
  • troelskn
    troelskn over 15 years
    Is subseq a CL function? It doesn't appear to be part of PLT-Scheme at least.
  • Corey
    Corey over 15 years
    Gauche has it: practical-scheme.net/wiliki/arcxref?subseq. don't know about others.
  • Inaimathi
    Inaimathi almost 14 years
    Keep in mind that with this answer, you can't do (slice '(1 2 3 4 5 6 7 8 9 0) 3 9) because it'll run out of list. You actually have to pass it an offset-from-start and length-of-final-list instead of two offsets from zero. You also can't do (slice l 3 -4) (which a python user would expect to mean "take everything from the third to the fourth-to-last element of list l"). A real slice function would be quite a bit more complicated.
  • Nathan Shively-Sanders
    Nathan Shively-Sanders almost 14 years
    You are thinking of Python slice, which uses offset1, offset2. For the offset, number-of-elements approach, this is the right answer: "take 9 elements starting at the 3rd" should give you fewer than 9 if the list runs out. (I personally think Python's approach is better, but that's not what the question is.)
  • frayser
    frayser almost 2 years
    In Racket's data/collection there's subsequence and subsequence*