Toshiro
Toshiro

Reputation: 345

If you're mapping a function over a list in rackect how can you get a reference to the next element?

If I have a list and I map a lambda function over it how can I get a reference to the next or previous item while processing the current one?

(map (lambda (x) x) '(1 2 3))

How would I reference the previous or next element while processing x?

Upvotes: 2

Views: 234

Answers (4)

GoZoner
GoZoner

Reputation: 70175

Start with your one list, construct two other lists, one 'shifted' right, and the other 'shifted' left. Like this:

(define (process func x)
  (let ((to-front (cons 'front (reverse (cdr (reverse x)))))
        (to-rear  (append (cdr x) (list 'rear))))
    (map func to-front x to-rear)))

Note that the stuff above with reverse is because map expects all lists to have the same length. So when adding to the front, you need to remove one from the tail.

Also, the provided func needs to accept three arguments.

> (process list '(a b c))
((front a b) (a b c) (b c rear))

Upvotes: 3

Sylwester
Sylwester

Reputation: 48745

John McCarthy originally made maplist and it's defined in CL still and predates map(car). It's definition in Scheme would be something like:

(define (maplist fun lst)
  (if (null? lst)
      '()
      (cons (fun lst) (maplist fun (cdr lst)))))

(maplist values '(1 2 3 4)) ; ==> ((1 2 3 4) (2 3 4) (3 4) (4))

It's slightly more difficult to get each element like map but if you need more than the first then it's perfect.

Upvotes: 4

Greg Hendershott
Greg Hendershott

Reputation: 16260

For simplicity let's take the case of two elements at a time -- the current and next one. So if you have (list 1 2 3), and a function that takes this and next args, you want it to be called with:

1 2
2 3
3 <some value, let's say 3>

You could write that concisely as:

(map f xs (append (drop xs 1) (list (last xs))))

However the drop and append-ing means that's not the fastest way to do it. Instead you could write a map-slide-pairs function to do it more directly:

#lang racket/base
(require racket/match)

;; map a list as "sliding pairs". For example:
;; (map-slide-pairs cons '(1 2 3)) ==> '((1 . 2)
;;                                       (2 . 3)
;;                                       (3 . 3))
(define (map-slide-pairs f xs #:last-val [last-val #f])
  ;; Concise implementation:
  ;;   (map f xs (append (drop xs 1) (list (last xs)))))
  ;; Faster implementation:
  (let loop ([xs xs])
    (match xs
      [(list) (list)]
      [(list this) (list (f this (or last-val this)))]
      [(list this next more ...) (cons (f this next)
                                       (loop (cons next more)))])))

(module+ test
  (require rackunit)
  (check-equal? (map-slide-pairs cons '(1 2 3))
                '([1 . 2][2 . 3][3 . 3]))
  (check-equal? (map-slide-pairs cons '(1 2 3) #:last-val 100)
                '([1 . 2][2 . 3][3 . 100])))

Hopefully you can see how to extend this and make a "map-slide-triples" function that would be called with the previous, current, and next elements of the list.

Upvotes: 2

nvlass
nvlass

Reputation: 685

You can always use map on two zipped lists, i.e.

(import (srfi srfi-1)) ; or use some zip implementation
(define a '(1 2 3 4 5))
(map (lambda (x) x)
  (zip a
       (append (cdr a) (list (car a)))))

which results in ((1 2) (2 3) (3 4) (4 5) (5 1)).

Of course, the above assumes "periodic" boundary conditions for the lists (you should modify the boundary conditions for your case). And also you would need to modify the lambda to handle pairs of elements.

Upvotes: 2

Related Questions