Flatten once procedure

2019-08-10 11:28发布

问题:

I'm having a bit of a struggle with coding a procedure that flattens a list once, i.e (flatten-once '((b) (c f) ((d)(e)))) would produce '(b c f (d) (e))). I checked up on a few sources on how the standard flatten procedure works but it implements functions that are not included in the intermediate student with lambda language form I'm required to use. As far as I have it figured out a foldr would be somewhat helpful and have managed to get this

(define (flatten-once lst)
  (cond
   [(empty? lst) lst]
   [else 
    ((foldr cons (first (rest lst)) (first lst)))]))

which returns '(b c f) , so I guess it flattens part of the list. I tried continuing the definition through recursion but that just gives errors, so I guess I'm missing something.

回答1:

The proposed code is overly complicated, resist the temptation to use folds everywhere, they're not the answer to everything - I say this because I've seen other of your questions and frequently, there's an unnecessary call to foldr or foldl. A simple append will do the trick:

(define (flatten-once lst)
  (apply append lst))

It works as expected:

(flatten-once '((b) (c f) ((d)(e))))
=> '(b c f (d) (e))

If the input list contains elements which are not lists, then a bit more of work needs to be done. To be extra-careful, we can do this:

(define (flatten-once lst)
  (apply append
         (map (lambda (e) (if (cons? e) e (list e)))
              lst)))

Now it'll work for inputs such as this one, noticing that the element a was added to the list. Another alternative would be to delete it from the list, if that makes more sense then replace (list e) with '() in the code above.

(flatten-once '(a (b) (c f) ((d)(e))))
=> '(a b c f (d) (e))

Finally, in the spirit of @Alex's answer, this second variant can also be written using foldr:

(define (flatten-once lst)
  (foldr (lambda (e acc)
           (append (if (cons? e) e (list e)) acc))
         '()
         lst))


回答2:

The trick is as Óscar López already said, to use append.

If you want to use a solution with foldr :

(define (flatten-once lst)
  (foldr append '() lst))

It should work as expected.



回答3:

Less elegant that Oscar's version, but without append (and twice as fast in my tests):

(define (flatten-once lst)
  (reverse
   (let loop ((lst lst) (first #t) (res null))
     (if (null? lst)
         res
         (let ((e (car lst)))
           (loop (cdr lst) 
                 first
                 (if (and first (cons? e))
                     (loop e #f res)
                     (cons e res))))))))

NB in intermediate student with lambda language this needs to be expressed as:

(define (flatten-once-helper lst first res)
  (if (null? lst)
      res
      (let ((e (car lst)))
        (flatten-once-helper 
         (cdr lst) 
         first
         (if (and first (cons? e))
             (flatten-once-helper e #f res)
             (cons e res))))))

(define (flatten-once lst)  
  (reverse (flatten-once-helper lst #t null)))