Trong sơ đồ, Chức năng "bản đồ" thường hữu ích để tính toán một danh sách dựa trên danh sách khác.
Trong thực tế, trong sơ đồ, bản đồ có một "n đối số" chức năng và "n" danh sách và gọi chức năng cho mỗi phần tử tương ứng của mỗi danh sách:
> (map * '(3 4 5) '(1 2 3))
(3 8 15)
Nhưng một bổ sung rất tự nhiên để đây sẽ là một chức năng "bản đồ Descartes", sẽ gọi hàm "n-argument" của bạn với tất cả các cách khác nhau để chọn một phần tử từ mỗi danh sách. Tôi đã mất một thời gian để tìm ra chính xác làm thế nào để làm điều đó, nhưng ở đây bạn đi:
; 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))))
Nếu không có tất cả các ý kiến và một số cú pháp định nghĩa hàm nhỏ gọn hơn chúng ta có:
(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)))
Tôi nghĩ ở trên là hợp lý "thanh lịch" ... cho đến khi ai đó cho tôi thấy định nghĩa Haskell tương đương:
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 dòng !!!
Tôi không tự tin về hiệu quả thực hiện của mình - đặc biệt là bước "làm phẳng" nhanh chóng viết nhưng có thể kết thúc cuộc gọi "nối thêm" với số lượng danh sách rất lớn, có thể có hoặc không hiệu quả trên một số sơ đồ triển khai .
Để có tính thực tiễn/hữu ích cuối cùng, bạn sẽ muốn có phiên bản "danh sách/luồng/vòng lặp được đánh giá một cách lười biếng" thay vì danh sách được chỉ định đầy đủ .... một chức năng "cartesian-map-stream" nếu bạn muốn, sau đó trả về một "dòng" của các kết quả ... nhưng điều này phụ thuộc vào ngữ cảnh (tôi đang nghĩ đến khái niệm "stream" như được giới thiệu trong SICP) ... và sẽ miễn phí từ phiên bản Haskell nhờ nó đánh giá lười biếng . Nói chung, trong Đề án, nếu bạn muốn "thoát ra" của vòng lặp tại một số điểm bạn cũng có thể sử dụng một sự tiếp tục (như ném một ngoại lệ nhưng nó được chấp nhận thực hành trong Đề án cho luồng điều khiển).
Tôi đã vui vẻ viết điều này!
Câu hỏi của bạn không phải là rất cụ thể. Bạn có thể cung cấp thêm thông tin về những gì bạn đang cố gắng làm và những gì bạn đang gặp sự cố không? –
Có lẽ bạn có thể thử viết nó với SRFI-42 và đăng nó ở đây để chứng minh những gì bạn muốn thực hiện? – grettke