-->

How to use append-map in Racket (Scheme)

2019-06-19 02:16发布

问题:

I don't fully understand what the append-map command does in racket, nor do I understand how to use it and I'm having a pretty hard time finding some decently understandable documentation online for it. Could someone possibly demonstrate what exactly the command does and how it works?

回答1:

The append-map procedure is useful for creating a single list out of a list of sublists after applying a procedure to each sublist. In other words, this code:

(append-map proc lst)

... Is semantically equivalent to this:

(apply append (map proc lst))

... Or this:

(append* (map proc lst))

The applying-append-to-a-list-of-sublists idiom is sometimes known as flattening a list of sublists. Let's look at some examples, this one is right here in the documentation:

(append-map vector->list '(#(1) #(2 3) #(4)))
'(1 2 3 4)

For a more interesting example, take a look at this code from Rosetta Code for finding all permutations of a list:

(define (insert l n e)
  (if (= 0 n)
      (cons e l)
      (cons (car l) 
            (insert (cdr l) (- n 1) e))))

(define (seq start end)
  (if (= start end)
      (list end)
      (cons start (seq (+ start 1) end))))

(define (permute l)
  (if (null? l)
      '(())
      (apply append (map (lambda (p)
                           (map (lambda (n)
                                  (insert p n (car l)))
                                (seq 0 (length p))))
                         (permute (cdr l))))))

The last procedure can be expressed more concisely by using append-map:

(define (permute l)
  (if (null? l)
      '(())
      (append-map (lambda (p)
                    (map (lambda (n)
                           (insert p n (car l)))
                         (seq 0 (length p))))
                  (permute (cdr l)))))

Either way, the result is as expected:

(permute '(1 2 3))
=> '((1 2 3) (2 1 3) (2 3 1) (1 3 2) (3 1 2) (3 2 1))


回答2:

In Common Lisp, the function is named "mapcan" and it is sometimes used to combine filtering with mapping:

* (mapcan (lambda (n) (if (oddp n) (list (* n n)) '()))
        '(0 1 2 3 4 5 6 7))
(1 9 25 49)

In Racket that would be:

> (append-map (lambda (n) (if (odd? n) (list (* n n)) '()))
            (range 8))
'(1 9 25 49)

But it's better to do it this way:

> (filter-map (lambda (n) (and (odd? n) (* n n))) (range 8))
'(1 9 25 49)