Paramorphisms là gì?


96

Đọc qua bài báo kinh điển này , tôi bị mắc kẹt với các phép biến hình. Thật không may, phần này khá mỏng, và trang Wikipedia không nói bất cứ điều gì.

Bản dịch Haskell của tôi là:

para :: (a -> [a] -> b -> b) -> b -> [a] -> b
para f base = h
  where
    h []       =   base
    h (x:xs)   =   f x xs (h xs)

Nhưng tôi không tìm kiếm điều đó - tôi không có bất kỳ trực giác nào về chữ ký kiểu hoặc kết quả mong muốn.

Mô hình là gì, và một số ví dụ hữu ích trong hành động là gì?


Có, tôi đã xem những câu hỏi này , nhưng chúng không đề cập trực tiếp đến các biến hình và chỉ trỏ đến các tài nguyên có thể hữu ích làm tài liệu tham khảo chứ không phải tài liệu học tập.


1
para f base xs = foldr (uncurry f) base $ zip xs (tail $tails xs), methinks.
Daniel Fischer

Theo trang wiki này , các paramorphisms "mô hình hóa đệ quy nguyên thủy trên các kiểu dữ liệu quy nạp". Điều đó có nghĩa là gì / giúp đỡ?
huon

4
Bài báo "Sự phân hạch" của Jeremy Gibbons mà tôi đã chỉ ra trong một nhận xét cho một trong những câu hỏi đó là tài liệu học tập rất hữu ích. cs.ox.ac.uk/jeremy.gibbons/publications/fission.pdf Nó hoạt động thông qua nhiều mẫu đệ quy rất rõ ràng.
stephen Tetley

1
Daniel's viết lại có thể được đơn giản hóa như para f base xs = foldr g base (init $ tails xs) where g (x:xs) = f x xs . Điều này gợi nhớ đến Common Lisp'smaplist .
Will Ness,

Câu trả lời:


110

Đúng vậy para. So sánh với catamorphism, hoặc foldr:

para  :: (a -> [a] -> b -> b) -> b -> [a] -> b
foldr :: (a ->        b -> b) -> b -> [a] -> b

para  c n (x : xs) = c x xs (para c n xs)
foldr c n (x : xs) = c x    (foldr c n xs)
para  c n []       = n
foldr c n []       = n

Một số người gọi các paramorphisms là "nguyên thủy đệ quy" trái ngược với catamorphisms ( foldr) là "lặp".

Trong foldrđó hai tham số được cung cấp một giá trị được tính toán đệ quy cho mỗi subobject đệ quy của dữ liệu đầu vào (đây, đó là phần cuối của danh sách), paracác tham số của nhận được cả subobject ban đầu và giá trị được tính toán đệ quy từ nó.

Một chức năng ví dụ được thể hiện độc đáo paralà tập hợp các số lượng thích hợp của một danh sách.

suff :: [x] -> [[x]]
suff = para (\ x xs suffxs -> xs : suffxs) []

vậy nên

suff "suffix" = ["uffix", "ffix", "fix", "ix", "x", ""]

Có thể đơn giản hơn vẫn là

safeTail :: [x] -> Maybe [x]
safeTail = para (\ _ xs _ -> Just xs) Nothing

trong đó nhánh "khuyết điểm" bỏ qua đối số được tính toán đệ quy của nó và chỉ trả về phần đuôi. Được đánh giá một cách lười biếng, việc tính toán đệ quy không bao giờ xảy ra và phần đuôi được trích xuất trong thời gian không đổi.

Bạn có thể xác định foldrbằng cách sử dụng parakhá dễ dàng; nó phức tạp hơn một chút để xác định paratừ đó foldr, nhưng chắc chắn là có thể và mọi người nên biết nó được thực hiện như thế nào!

foldr c n =       para  (\ x  xs  t ->           c x    t)       n
para  c n = snd . foldr (\ x (xs, t) -> (x : xs, c x xs t)) ([], n)

Bí quyết để xác định paravới foldrlà để tái tạo một bản sao của dữ liệu gốc, để chúng ta truy cập vào một bản sao của đuôi ở mỗi bước, mặc dù chúng ta không có quyền truy cập vào bản gốc. Cuối cùng, sndloại bỏ bản sao của đầu vào và chỉ cung cấp giá trị đầu ra. Nó không hiệu quả lắm, nhưng nếu bạn quan tâm đến tính biểu hiện tuyệt đối, nó không paramang lại nhiều hơn foldr. Nếu bạn sử dụng foldrphiên bản -encoded này para, sau cùng safeTailsẽ mất thời gian tuyến tính, sao chép phần tử đuôi từng phần tử.

Vậy là xong: paralà một phiên bản tiện lợi hơn foldrcho phép bạn truy cập ngay vào phần đuôi của danh sách cũng như giá trị được tính từ đó.

Trong trường hợp chung, làm việc với một kiểu dữ liệu được tạo như điểm cố định đệ quy của một hàm

data Fix f = In (f (Fix f))

bạn có

cata :: Functor f => (f         t  -> t) -> Fix f -> t
para :: Functor f => (f (Fix f, t) -> t) -> Fix f -> t

cata phi (In ff) = phi (fmap (cata phi) ff)
para psi (In ff) = psi (fmap keepCopy   ff) where
  keepCopy x = (x, para psi x)

và một lần nữa, cả hai đều có thể xác định được lẫn nhau, parađược định nghĩa catabởi cùng một thủ thuật "tạo bản sao"

para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt))

Một lần nữa, parakhông có gì diễn đạt hơn cata, nhưng thuận tiện hơn nếu bạn cần truy cập dễ dàng vào các cấu trúc con của đầu vào.

Chỉnh sửa: Tôi nhớ một ví dụ hay khác.

Xem xét cây tìm kiếm nhị phân được cung cấp bởi Fix TreeFnơi

data TreeF sub = Leaf | Node sub Integer sub

và thử xác định chèn cho cây tìm kiếm nhị phân, trước tiên là a cata, sau đó là a para. Bạn sẽ tìm thấy paraphiên bản dễ dàng hơn nhiều, vì tại mỗi nút, bạn sẽ cần phải chèn vào một cây con nhưng giữ nguyên trạng thái còn lại.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.