Tổ hợp là gì và chúng được áp dụng như thế nào cho các dự án lập trình? (giải thích thực tế)


51

Tổ hợp là gì?

Tôi đang tìm:

  • một lời giải thích thực tế
  • ví dụ về cách chúng được sử dụng
  • ví dụ về cách các tổ hợp cải thiện chất lượng / tính tổng quát của mã

Tôi không tìm kiếm:

  • giải thích về các tổ hợp không giúp tôi hoàn thành công việc (chẳng hạn như tổ hợp Y)

Bộ kết hợp tương tự như "trạng từ", các hàm nhận chức năng sau đó trả về các chức năng khác. Chúng có thể giúp loại bỏ mã trùng lặp vì bạn không cần ở giữa các biến. Một số hữu ích là hai lần (f) = \ x -> f (f (x)), flip (op) -> \ xy -> y op x, (.) Như trong (fg) x = f (g (x (x) )), ($) có thể trợ giúp với bản đồ (được gọi là <$> trong infix) như trong ($ 5) <$> [(+1), (* 2)] = [6, 10], cà ri có thể được sử dụng trong Lisp / Python / JavaScript cho ứng dụng một phần và unurry có thể được sử dụng cho các hàm yêu cầu bản ghi (bộ dữ liệu) trong Haskell. Khi x |> f = fa, x |> (chiều dài &&& sum) |> unurry (/) là mức trung bình.
aoeu256

Câu trả lời:


51

Từ một tổ hợp quan điểm thực tế là loại cấu trúc lập trình cho phép bạn kết hợp các phần logic trong cách cư xử thú vị và thường tiên tiến. Thông thường sử dụng chúng phụ thuộc vào khả năng có thể đóng gói mã thực thi vào các đối tượng, thường được gọi là (vì lý do lịch sử) các hàm lambda hoặc biểu thức lambda, nhưng số dặm của bạn có thể thay đổi.

Một ví dụ đơn giản về bộ kết hợp (hữu ích) là một hàm có hai hàm lambda không có tham số và tạo một hàm mới chạy chúng theo thứ tự. Bộ kết hợp thực tế nhìn vào mã giả chung như thế này:

func in_sequence(first, second):
  lambda ():
    first()
    second()

Điều cốt yếu làm cho điều này trở thành một tổ hợp là hàm ẩn danh (hàm lambda) trên dòng thứ hai; khi bạn gọi

a = in_sequence(f, g)

đối tượng kết quả a không phải là kết quả của việc chạy f () đầu tiên và sau đó là g (), nhưng nó là một đối tượng mà bạn có thể gọi sau để thực hiện f () và g () theo thứ tự:

a() // a is a callable object, i.e. a function without parameters

Tương tự như vậy, bạn có thể có một tổ hợp chạy song song hai khối mã:

func in_parallel(first, second):
  lambda ():
    t1 = start_thread(first)
    t2 = start_thread(second)
    wait(t1)
    wait(t2)

Và sau đó một lần nữa,

a = in_parallel(f, g)
a()

Điều thú vị là 'in_pool' và 'in_ resultence' đều là các tổ hợp có cùng loại / chữ ký, tức là cả hai đều lấy hai đối tượng hàm không tham số và trả về một đối tượng hàm mới. Bạn thực sự có thể viết những thứ như

a = in_sequence(in_parallel(f, g), in_parallel(h, i))

và nó hoạt động như mong đợi.

Về cơ bản, các tổ hợp cho phép bạn xây dựng luồng điều khiển của chương trình (trong số những thứ khác) theo cách thức thủ tục và linh hoạt. Ví dụ: nếu bạn sử dụng bộ kết hợp in_pool (..) để chạy song song trong chương trình của mình, bạn có thể thêm gỡ lỗi liên quan đến việc đó để thực hiện chính trình kết hợp in_pool. Sau này, nếu bạn nghi ngờ rằng chương trình của bạn có lỗi liên quan đến song song, bạn thực sự có thể thực hiện lại in_pool:

in_parallel(first, second):
  in_sequence(first, second)

và với một nét, tất cả các phần song song đã được chuyển đổi thành các phần tiếp theo!

Kết hợp rất hữu ích khi sử dụng đúng.

Bộ kết hợp Y, tuy nhiên, không cần thiết trong cuộc sống thực. Nó là một bộ kết hợp cho phép bạn tạo các hàm tự đệ quy và bạn có thể tạo chúng dễ dàng bằng bất kỳ ngôn ngữ hiện đại nào mà không cần bộ kết hợp Y.


9

Thật sai lầm khi gắn nhãn hiệu Y-combinator như một thứ không "giúp hoàn thành công việc". Tôi đã tìm thấy nó rất hữu ích trong một số dịp. Trường hợp rõ ràng nhất là khi bạn phải nhanh chóng khởi động một số ngôn ngữ được dịch. Nếu bạn cung cấp một tập tối thiểu của nguyên thủy, cụ thể là sequence, select, call, constclosure allocation, nó đã đủ để xây dựng hoàn chỉnh, ngôn ngữ phức tạp tùy ý. Không cần hỗ trợ đặc biệt cho đệ quy - nó có thể được thêm thông qua bộ kết hợp điểm cố định. Nếu không, bạn sẽ cần nhiều nguyên thủy phức tạp hơn nhiều.

Một trường hợp rõ ràng khác cho tổ hợp là obfuscation. Một mã được dịch vào phép tính SKI thực tế không thể đọc được. Nếu bạn thực sự phải làm xáo trộn việc thực hiện thuật toán, hãy xem xét sử dụng các tổ hợp, đây là một ví dụ .

Và, tất nhiên, tổ hợp là một công cụ quan trọng để thực hiện các ngôn ngữ chức năng. Cách tiếp cận đơn giản nhất (như trong ví dụ trên) là thông qua SKI hoặc phép tính tương đương. Supercombinators được sử dụng trong một số triển khai khác. Cuốn sách này nói về nó một cách sâu sắc.

Đây là một trò đùa , nhưng một trò đùa đáng để đọc rất cẩn thận, vì nhiều kỹ thuật và lý thuyết lập trình phức tạp được đề cập ở đó.


1
@MattFenwick, một nhu cầu thả vào một trình thông dịch nhúng đơn giản thường phát sinh nơi bạn sẽ không bao giờ mong đợi nó. Ví dụ, trong trường hợp của tôi, đó là ngôn ngữ tôi phải thiết kế để mở rộng giao thức giao tiếp. IPC đơn giản là không đủ, vì vậy giao thức phải được thực thi.
SK-logic

@MattFenwick, như đối với câu hỏi của bạn: bạn có thể thử viết một số mã bằng APL hoặc J. Bộ kết hợp rất cần thiết ở đó, vì vậy bạn sẽ có ý tưởng về cách áp dụng chúng đúng cách. Ngoài ra, đọc theo kiểu không có điểm có thể giúp: en.wikipedia.org/wiki/Tacit_programming
SK-logic

7

Tìm hiểu một chút, tôi tìm thấy một câu hỏi StackOverflow, Giải thích tốt về Com Comatorsators (Đối với những người không phải là nhà toán học) đó là anh em họ thân của câu hỏi này. Một trong những câu trả lời được chỉ ra trên blog của Reginald Braithwaite, Homoiconic , liên kết đến một số ví dụ hữu ích về mã kết hợp trong mã (ví dụ: trình kết hợp K , được thực hiện theo Object#tapphương pháp của Ruby - đọc trang này để biết ví dụ về lý do tại sao nó hữu ích).

Các trang Wikipedia trên combinatory logic mô tả combinators toàn cầu hơn.


Điều này giải quyết điểm đạn thứ hai trong câu hỏi của tôi. Cảm ơn ví dụ!

1
Bài đăng này có một số liên kết tốt nhưng không thực sự trả lời câu hỏi trực tiếp. Câu trả lời nên được tự hoàn thành và sử dụng các liên kết làm tài liệu tham khảo nếu cần thiết.
Aaronaught
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.