Вложенные циклы схемы/Lisp и рекурсия

Вы можете вызывать super () с параметрами, если хотите вызвать конструктор суперкласса не по умолчанию или если у суперкласса нет конструктора по умолчанию. Компилятор может вставить только конструктор по умолчанию, без аргументов super ().

6
задан Svante 1 November 2009 в 23:55
поделиться

3 ответа

На схеме, Функция "map" часто удобна для вычисления одного списка на основе другого.

Фактически, в схеме map принимает функцию "n-аргументов" и "n" списков и вызывает function for each corresponding element of each list:

> (map * '(3 4 5) '(1 2 3))
(3 8 15)

But a very natural addition to this would be a "cartesian-map" function, which would call your "n-argument" function with all of the different ways of picking one element from each list. It took me a while to figure out exactly how to do it, but here you go:

; curry takes:
;  * a p-argument function AND
;  * n actual arguments,
; and returns a function requiring only (p-n) arguments
; where the first "n" arguments are already bound. A simple
; example
; (define add1 (curry + 1))
; (add1 3)
;  => 4
; Many other languages implicitly "curry" whenever you call
; a function with not enough arguments.
(define curry
    (lambda (f . c) (lambda x (apply f (append c x)))))

; take a list of tuples and an element, return another list
; with that element stitched on to each of the tuples:
; e.g.
; > (stitch '(1 2 3) 4)
; ((4 . 1) (4 . 2) (4 . 3))
(define stitch
    (lambda (tuples element)
        (map (curry cons element) tuples)))

; Flatten takes a list of lists and produces a single list
; e.g.
; > (flatten '((1 2) (3 4)))
; (1 2 3 4)
(define flatten
    (curry apply append))

; cartesian takes two lists and returns their cartesian product
; e.g.
; > (cartesian '(1 2 3) '(4 5))
; ((1 . 4) (1 . 5) (2 . 4) (2 . 5) (3 . 4) (3 . 5))
(define cartesian
    (lambda (l1 l2)
        (flatten (map (curry stitch l2) l1))))

; cartesian-lists takes a list of lists
; and returns a single list containing the cartesian product of all of the lists.
; We start with a list containing a single 'nil', so that we create a
; "list of lists" rather than a list of "tuples".

; The other interesting function we use here is "fold-right" (sometimes called
; "foldr" or "reduce" in other implementations). It can be used
; to collapse a list from right to left using some binary operation and an
; initial value.
; e.g.
; (fold-right cons '() '(1 2 3))
; is equivalent to
; ((cons 1 (cons 2 (cons 3 '())))
; In our case, we have a list of lists, and our binary operation is to get the
; "cartesian product" between each list.
(define cartesian-lists
    (lambda (lists)
        (fold-right cartesian '(()) lists)))

; cartesian-map takes a n-argument function and n lists
; and returns a single list containing the result of calling that
; n-argument function for each combination of elements in the list:
; > (cartesian-map list '(a b) '(c d e) '(f g))
; ((a c f) (a c g) (a d f) (a d g) (a e f) (a e g) (b c f)
;  (b c g) (b d f) (b d g) (b e f) (b e g))
(define cartesian-map
    (lambda (f . lists)
        (map (curry apply f) (cartesian-lists lists))))

Without all the comments and some more compact function definition syntax we have:

(define (curry f . c) (lambda x (apply f (append c x))))
(define (stitch tuples element)
        (map (curry cons element) tuples))
(define flatten (curry apply append))
(define (cartesian l1 l2)
        (flatten (map (curry stitch l2) l1)))
(define cartesian-lists (curry fold-right cartesian '(()))))
(define (cartesian-map f . lists)
        (map (curry apply f) (cartesian-lists lists)))

I thought the above was reasonably "elegant"... until someone showed me the equivalent Haskell definition:

cartes f (a:b:[]) = [ f x y | x <- a , y <- b ] 
cartes f (a:b:bs) = cartes f ([ f x y | x <- a , y <- b ]:bs) 

2 lines!!!

I am not so confident on the efficiency of my implementation - particularly the "flatten" step was quick to write but could end up calling "append" with a very large number of lists, which may or may not be very efficient on some Scheme implementations.

For ultimate practicality/usefulness you would want a version that could take "lazily evaluated" lists/streams/iterator rather than fully specified lists.... a "cartesian-map-stream" function if you like, that would then return a "stream" of the results... but this depends on the context (I am thinking of the "stream" concept as introduced in SICP)... and would come for free from the Haskell version thanks to it's lazy evaluation.

In general, in Scheme, if you wanted to "break out" of the looping at some point you could also use a continuation (like throwing an exception but it is accepted practise in Scheme for control flow).

I had fun writing this!

14
ответ дан 8 December 2019 в 13:00
поделиться

Я не уверен, что понимаю, в чем проблема. Я считаю, что главное, что вы должны понимать в функциональном программировании, это: создавать сложные функции, составляя несколько более простых функций.

Например, в этом случае:

;compute the list of the (x,y) for y in l
(define (pairs x l)
  (define (aux accu x l)
    (if (null? l)
        accu
        (let ((y (car l))
              (tail (cdr l)))
          (aux (cons (cons x y) accu) x tail))))
  (aux '() x l))

(define (cartesian-product l m)   
  (define (aux accu l)
    (if (null? l) 
        accu
        (let ((x (car l)) 
              (tail (cdr l)))
          (aux (append (pairs x m) accu) tail))))
  (aux '() l))       

Вы определяете различные шаги: чтобы получить декартово произведение, если вы "цикл" по первому списку, вам нужно будет вычислить список (x, y) , для y во втором списке.

]
2
ответ дан 8 December 2019 в 13:00
поделиться

Здесь уже есть несколько хороших ответов, но для простых вложенных функций (например, хвостовой рекурсивный факториал) я предпочитаю именованный let:

(define factorial  
  (lambda (n)
    (let factorial-impl ([n n] [t 1])
      (if (eq? n 0)
        t
        (factorial-impl (- n 1) (* t n))))))
2
ответ дан 8 December 2019 в 13:00
поделиться