Câu hỏi Lập trình phản ứng (chức năng) là gì?


Tôi đã đọc bài viết trên Wikipedia lập trình phản ứng. Tôi cũng đã đọc bài viết nhỏ trên chức năng lập trình phản ứng. Các mô tả khá trừu tượng.

  1. Lập trình phản ứng chức năng (FRP) có nghĩa là gì trong thực tế?
  2. Lập trình phản ứng (trái ngược với lập trình không phản ứng là gì?) Bao gồm?

Nền của tôi là các ngôn ngữ bắt buộc / OO, do đó, một lời giải thích liên quan đến mô hình này sẽ được đánh giá cao.


1149
2018-06-22 16:41


gốc


đây là một chàng trai với một trí tưởng tượng năng động và kỹ năng kể chuyện tốt có trên toàn bộ điều. paulstovell.com/reactive-programming - melaos
Ai đó thực sự cần phải viết một "Lập trình phản ứng chức năng cho người giả" cho tất cả chúng ta tự động hóa ở đây. Mọi tài nguyên tôi đã tìm thấy, ngay cả Elm, dường như cho rằng bạn đã nhận được một Master trong CS trong năm năm qua. Những người am hiểu về FRP dường như đã hoàn toàn mất khả năng nhìn thấy vấn đề từ quan điểm ngây thơ, điều gì đó quan trọng đối với việc giảng dạy, đào tạo và truyền giáo. - TechZen
Một phần giới thiệu FRP tuyệt vời khác: Giới thiệu về Lập trình phản ứng mà bạn đã bỏ lỡ bởi đồng nghiệp của tôi André - Jonik
Một trong những điều tốt nhất tôi đã thấy, Ví dụ dựa trên: gist.github.com/staltz/868e7e9bc2a7b8c1f754 - Razmig
Tôi thấy bảng tính tương tự rất hữu ích như một ấn tượng thô đầu tiên (xem câu trả lời của Bob: stackoverflow.com/a/1033066/1593924). Một ô bảng tính phản ứng với những thay đổi trong các ô khác (kéo) nhưng không tiếp cận và thay đổi những người khác (không đẩy). Kết quả cuối cùng là bạn có thể thay đổi một ô và một tỷ người khác 'độc lập' cập nhật màn hình của riêng họ. - Jon Coombs


Các câu trả lời:


Nếu bạn muốn có cảm giác về FRP, bạn có thể bắt đầu với Fran hướng dẫn từ năm 1998, có hình minh họa động. Đối với giấy tờ, hãy bắt đầu bằng Chức năng hoạt hình phản ứng và sau đó theo dõi các liên kết trên các ấn phẩm liên kết trên trang chủ của tôi và FRP liên kết trên Haskell wiki.

Cá nhân tôi thích nghĩ về FRP có nghĩa trước khi giải quyết cách nó có thể được triển khai. (Mã không có đặc tả là câu trả lời không có câu hỏi và do đó "không sai"). Vì vậy, tôi không mô tả FRP trong các thuật ngữ đại diện / triển khai như Thomas K thực hiện trong một câu trả lời khác (biểu đồ, nút, cạnh, kích hoạt, thực thi, v.v.). Có nhiều kiểu triển khai có thể, nhưng không có triển khai nào nói FRP là gì .

Tôi cộng hưởng với mô tả đơn giản của Laurence G rằng FRP là về "các kiểu dữ liệu đại diện cho một giá trị 'theo thời gian'". Lập trình bắt buộc thông thường chỉ nắm bắt các giá trị động này một cách gián tiếp, thông qua trạng thái và đột biến. Lịch sử hoàn chỉnh (quá khứ, hiện tại, tương lai) không có đại diện lớp học đầu tiên. Hơn nữa, chỉ phát triển đặc biệt các giá trị có thể được (gián tiếp) bị bắt, vì mô hình mệnh lệnh là tạm thời rời rạc. Ngược lại, FRP nắm bắt các giá trị phát triển này trực tiếp và không gặp khó khăn gì với liên tục giá trị phát triển.

FRP cũng là bất thường ở chỗ nó đồng thời mà không chạy afoul của tổ chuột lý thuyết và thực dụng mà bệnh dịch đồng thời bắt buộc. Về mặt ngữ nghĩa, đồng thời của FRP là tinh xảo, xác địnhliên tiếp. (Tôi đang nói về ý nghĩa, không thực hiện. Việc triển khai có thể hoặc có thể không liên quan đến đồng thời hoặc song song.) Xác định ngữ nghĩa là rất quan trọng cho lý luận, cả hai đều nghiêm ngặt và không chính thức. Trong khi đồng thời cho biết thêm phức tạp rất lớn để lập trình bắt buộc (do interleaving nondeterministic), nó là nỗ lực trong FRP.

Vì vậy, FRP là gì? Bạn có thể tự mình phát minh ra nó. Bắt đầu với những ý tưởng này:

  • Giá trị động / phát triển (tức là, giá trị "theo thời gian") là giá trị lớp học đầu tiên trong chính chúng. Bạn có thể định nghĩa chúng và kết hợp chúng, chuyển chúng vào và ra khỏi các hàm. Tôi gọi những thứ này là "hành vi".

  • Các hành vi được tạo ra từ một số nguyên thủy, như các hành vi và thời gian (tĩnh) liên tục (như đồng hồ), và sau đó với sự kết hợp tuần tự và song song. n các hành vi được kết hợp bằng cách áp dụng một hàm n-ary (trên các giá trị tĩnh), "điểm khôn ngoan", tức là, liên tục theo thời gian.

  • Để giải thích các hiện tượng rời rạc, có một loại (gia đình) khác của "sự kiện", mỗi loại có một dòng (hữu hạn hoặc vô hạn) của các lần xuất hiện. Mỗi lần xuất hiện có thời gian và giá trị liên quan.

  • Để đưa ra các từ vựng tổng hợp, trong đó tất cả các hành vi và sự kiện có thể được xây dựng, hãy chơi với một số ví dụ. Giữ deconstructing thành miếng tổng quát hơn / đơn giản.

  • Vì vậy, bạn biết bạn đang ở trên nền tảng vững chắc, cho toàn bộ mô hình một nền tảng tổng hợp, sử dụng kỹ thuật ngữ nghĩa học, nghĩa là (a) mỗi loại có một loại toán học đơn giản và chính xác tương ứng, và ( b) mỗi nguyên thủy và toán tử có một nghĩa đơn giản và chính xác như một hàm của ý nghĩa của các thành phần. Chưa bao giờ trộn các cân nhắc triển khai vào quá trình thăm dò của bạn. Nếu mô tả này là vô nghĩa với bạn, hãy tham khảo ý kiến ​​(a) Thiết kế denotational với các loại hình thái học, (b) Lập trình phản ứng chức năng push-pull (bỏ qua các bit triển khai) và (c) Denantational Semantics Trang wikibook Haskell. Cẩn thận rằng ngữ nghĩa denotational có hai phần, từ hai nhà sáng lập Christopher Strachey và Dana Scott: phần Strachey dễ dàng và hữu ích hơn và phần Scott khó hơn và ít hữu ích hơn (cho phần mềm thiết kế).

Nếu bạn gắn bó với những nguyên tắc này, tôi hy vọng bạn sẽ nhận được một cái gì đó nhiều hơn hoặc ít hơn trong tinh thần của FRP.

Tôi lấy những nguyên tắc này ở đâu? Trong thiết kế phần mềm, tôi luôn hỏi cùng một câu hỏi: "nó có nghĩa là gì?". Ngữ nghĩa học đã cho tôi một khuôn khổ chính xác cho câu hỏi này, và một khuôn khổ phù hợp với tính thẩm mỹ của tôi (không giống như ngữ nghĩa hoạt động hoặc tiên đề, cả hai đều khiến tôi không hài lòng). Vì vậy, tôi tự hỏi mình hành vi là gì? Tôi sớm nhận ra rằng tính chất rời rạc tạm thời của tính toán bắt buộc là một chỗ ở cho một phong cách đặc biệt của máy móc, chứ không phải là mô tả tự nhiên về hành vi. Mô tả chính xác đơn giản nhất về hành vi mà tôi có thể nghĩ đơn giản là "chức năng của thời gian (liên tục)", vì vậy đó là mô hình của tôi. Thật thú vị, mô hình này xử lý liên tục, xác định đồng thời một cách dễ dàng và ân sủng.

Đó là một thách thức khá lớn để thực hiện mô hình này một cách chính xác và hiệu quả, nhưng đó là một câu chuyện khác.


932
2018-06-23 04:31



Tôi đã nhận thức được lập trình hoạt động chức năng. Nó có vẻ liên quan đến nghiên cứu của riêng tôi (trong đồ họa thống kê tương tác) và tôi chắc chắn nhiều ý tưởng sẽ hữu ích cho công việc của tôi. Tuy nhiên, tôi thấy rất khó để vượt qua ngôn ngữ - tôi có thực sự học về "ngữ nghĩa học" và "loại hình thái học" để hiểu chuyện gì đang diễn ra không? Việc giới thiệu đối tượng chung về chủ đề sẽ rất hữu ích. - hadley
@Conal: bạn rõ ràng biết những gì bạn đang nói về, nhưng ngôn ngữ của bạn giả định tôi có một vị tiến sĩ trong toán học tính toán, mà tôi không. Tôi có nền tảng về kỹ thuật hệ thống và hơn 20 năm kinh nghiệm với máy tính và ngôn ngữ lập trình, tôi vẫn cảm thấy phản ứng của bạn khiến tôi bối rối. Tôi thách thức bạn để repost trả lời của bạn bằng tiếng Anh ;-) - mindplay.dk
@ minplay.dk: Các nhận xét của bạn không cho tôi nhiều điều để nói về những gì đặc biệt bạn không hiểu, và tôi không thích nghi ngờ về những gì mà bạn đang tìm kiếm. Tuy nhiên, tôi mời bạn nói cụ thể những khía cạnh nào trong lời giải thích của tôi ở trên mà bạn đang vấp phải, để tôi và những người khác có thể giúp bạn. Ví dụ: có những từ cụ thể bạn muốn xác định hoặc khái niệm mà bạn muốn thêm tài liệu tham khảo? Tôi thực sự làm như cải thiện sự rõ ràng và khả năng tiếp cận của văn bản của tôi - mà không làm giảm nó xuống. - Conal
"Xác định" / "xác định" có nghĩa là có một giá trị chính xác, được xác định chính xác. Ngược lại, hầu như tất cả các dạng đồng thời bắt buộc đều có thể đưa ra các câu trả lời khác nhau, tùy thuộc vào một bộ lập lịch hoặc cho dù bạn đang tìm kiếm hay không, và chúng thậm chí có thể bế tắc. "Semantic" (và cụ thể hơn là "denotational") đề cập đến giá trị ("ký hiệu") của một biểu thức hoặc biểu diễn, trái ngược với "hoạt động" (cách tính câu trả lời hoặc bao nhiêu không gian và / hoặc thời gian loại máy). - Conal
Tôi đồng ý với @ mindplay.dk mặc dù tôi không thể khoe khoang về việc đã ở trong lĩnh vực này rất lâu. Mặc dù nó có vẻ như bạn biết những gì bạn đang nói về, nó đã không cho tôi một sự hiểu biết nhanh chóng, ngắn gọn và đơn giản về những gì này là, như tôi tha hồ, đủ để mong đợi trên SO. Câu trả lời này chủ yếu đưa tôi đến một tấn câu hỏi mới mà không thực sự trả lời câu hỏi đầu tiên của tôi. Tôi hy vọng rằng việc chia sẻ trải nghiệm vẫn còn tương đối dốt nát trong lĩnh vực này có thể cung cấp cho bạn một cái nhìn sâu sắc về cách đơn giản và ngắn gọn mà bạn thực sự cần. Tôi đến từ một nền tảng tương tự như OP, btw. - Aske B.


Trong lập trình chức năng thuần túy, không có tác dụng phụ. Đối với nhiều loại phần mềm (ví dụ, bất kỳ thứ gì có tương tác với người dùng), các tác dụng phụ là cần thiết ở một mức độ nào đó.

Một cách để có được tác dụng phụ như hành vi trong khi vẫn giữ lại một phong cách chức năng là sử dụng lập trình phản ứng chức năng. Đây là sự kết hợp giữa lập trình chức năng và lập trình phản ứng. (Bài viết trên Wikipedia mà bạn liên kết là về bài viết sau.)

Ý tưởng cơ bản đằng sau lập trình phản ứng là có một số kiểu dữ liệu đại diện cho một giá trị "theo thời gian". Các tính toán liên quan đến các giá trị thay đổi theo thời gian này sẽ tự có các giá trị thay đổi theo thời gian.

Ví dụ: bạn có thể biểu diễn tọa độ chuột dưới dạng cặp giá trị theo thời gian nguyên. Hãy nói rằng chúng tôi đã có một cái gì đó như thế (đây là mã giả):

x = <mouse-x>;
y = <mouse-y>;

Tại bất kỳ thời điểm nào, x và y sẽ có tọa độ của con chuột. Không giống như lập trình không phản ứng, chúng tôi chỉ cần thực hiện nhiệm vụ này một lần và các biến x và y sẽ tự động cập nhật. Đây là lý do tại sao lập trình phản ứng và lập trình hàm hoạt động rất tốt với nhau: lập trình phản ứng loại bỏ sự cần thiết phải biến đổi biến trong khi vẫn cho phép bạn thực hiện rất nhiều những gì bạn có thể thực hiện với các biến đột biến.

Nếu sau đó chúng tôi thực hiện một số tính toán dựa trên giá trị kết quả này cũng sẽ là các giá trị thay đổi theo thời gian. Ví dụ:

minX = x - 16;
minY = y - 16;
maxX = x + 16;
maxY = y + 16;

Trong ví dụ này, minX sẽ luôn nhỏ hơn 16 so với tọa độ x của con trỏ chuột. Với các thư viện có khả năng phản ứng, bạn có thể nói như sau:

rectangle(minX, minY, maxX, maxY)

Và một hộp 32x32 sẽ được vẽ xung quanh con trỏ chuột và sẽ theo dõi nó bất cứ nơi nào nó di chuyển.

Đây là một điều khá tốt giấy về lập trình phản ứng chức năng.


740
2018-06-22 18:06



Vì vậy, lập trình phản ứng là một hình thức lập trình khai báo sau đó? - troelskn
> Vì vậy, lập trình phản ứng là một hình thức lập trình khai báo sau đó? Chức năng lập trình phản ứng là một dạng lập trình chức năng, là một dạng lập trình khai báo. - Conal
@ user712092 Không thực sự, không. Ví dụ, nếu tôi gọi sqrt(x) trong C với macro của bạn, chỉ tính toán sqrt(mouse_x()) và trả lại cho tôi gấp đôi. Trong một hệ thống phản ứng chức năng thực sự, sqrt(x) sẽ trả về "gấp đôi theo thời gian" mới. Nếu bạn cố gắng mô phỏng một hệ thống FR với #definebạn khá nhiều phải thề các biến trong lợi của macro. Các hệ thống FR cũng thường sẽ tính toán lại các công cụ khi nó cần được tính toán lại, trong khi sử dụng các macro có nghĩa là bạn sẽ liên tục đánh giá lại mọi thứ, tất cả các con đường xuống các biểu thức con. - Laurence Gonsalves
"Đối với nhiều loại phần mềm (ví dụ, bất kỳ thứ gì có tương tác với người dùng), các tác dụng phụ là cần thiết ở một mức độ nào đó." Và có lẽ chỉ ở cấp độ thực hiện. Có rất nhiều tác dụng phụ trong việc thực hiện lập trình chức năng thuần túy, lười biếng, và một trong những thành công của mô hình là để giữ cho nhiều hiệu ứng đó trong mô hình lập trình. Sự đột nhập của riêng tôi vào giao diện người dùng chức năng cho thấy rằng chúng cũng có thể được lập trình hoàn toàn mà không có tác dụng phụ. - Conal
@tieTYT x không bao giờ được gán lại / đột biến. Giá trị của x là chuỗi các giá trị theo thời gian. Một cách khác để xem xét nó là thay vì x có một giá trị "bình thường", giống như một số, giá trị của x là (khái niệm) một hàm cần có thời gian như một tham số. (Đây là một chút quá đơn giản. Bạn không thể tạo ra các giá trị thời gian cho phép bạn dự đoán tương lai của những thứ như vị trí chuột.) - Laurence Gonsalves


Một cách dễ dàng để đạt được trực giác đầu tiên về những gì nó giống như là để tưởng tượng chương trình của bạn là một bảng tính và tất cả các biến của bạn là các ô. Nếu bất kỳ ô nào trong bảng tính thay đổi, bất kỳ ô nào tham chiếu đến ô đó cũng sẽ thay đổi. Nó chỉ giống với FRP. Bây giờ hãy tưởng tượng rằng một số tế bào tự thay đổi (hay đúng hơn, được lấy từ thế giới bên ngoài): trong một tình huống GUI, vị trí của con chuột sẽ là một ví dụ tốt.

Điều đó nhất thiết phải bỏ lỡ khá nhiều. Ẩn dụ phá vỡ khá nhanh khi bạn thực sự sử dụng một hệ thống FRP. Đối với một, thường có những nỗ lực để mô hình các sự kiện rời rạc (ví dụ như chuột được nhấp). Tôi chỉ đưa nó vào đây để cho bạn biết nó như thế nào.


144
2018-06-23 14:52



Một ví dụ cực kỳ hữu ích. Thật tuyệt vời khi có những công cụ lý thuyết, và có lẽ một số người có được những tác động của điều đó mà không đòi hỏi một ví dụ nền tảng, nhưng tôi cần phải bắt đầu với những gì nó làm cho tôi, chứ không phải những gì nó trừu tượng. Những gì tôi chỉ mới nhận được (từ các cuộc đàm phán Rx của Netflix!) Là RP (hoặc Rx, anyway), làm cho những "thay đổi giá trị" lớp đầu tiên và cho phép bạn lý do về họ, hoặc viết các chức năng mà làm việc với họ. Viết các hàm để tạo bảng tính hoặc ô, nếu bạn muốn. Và nó xử lý khi một giá trị kết thúc (biến mất) và cho phép bạn dọn dẹp tự động. - Benjohn
Ví dụ này nhấn mạnh sự khác biệt giữa lập trình hướng sự kiện và cách tiếp cận phản ứng, nơi bạn chỉ khai báo các phụ thuộc để sử dụng định tuyến thông minh. - wildloop


Đối với tôi, nó có nghĩa là về 2 ý nghĩa khác nhau của biểu tượng =:

  1. Trong toán học x = sin(t) có nghĩa là, x Là tên khác cho sin(t). Vì vậy, viết x + y cũng giống như sin(t) + y. Lập trình hoạt động chức năng giống như toán học trong khía cạnh này: nếu bạn viết x + y, nó được tính với giá trị của t tại thời điểm nó được sử dụng.
  2. Trong ngôn ngữ lập trình giống C (ngôn ngữ bắt buộc), x = sin(t) là một nhiệm vụ: nó có nghĩa là x lưu trữ giá trị của  sin(t) được thực hiện tại thời điểm chuyển nhượng.

132
2018-05-25 14:52



Lời giải thích hay. Tôi nghĩ rằng bạn cũng có thể thêm rằng "thời gian" theo nghĩa FRP thường là "bất kỳ thay đổi nào từ đầu vào bên ngoài". Bất cứ lúc nào một lực lượng bên ngoài thay đổi một đầu vào của FRP, bạn đã di chuyển "thời gian" về phía trước, và tính toán lại tất cả mọi thứ một lần nữa bị ảnh hưởng bởi sự thay đổi. - Didier A.
Trong toán học x = sin(t) có nghĩa x là giá trị của sin(t) cho t. Nó là không phải một tên khác cho sin(t) làm chức năng. Nếu không nó sẽ là x(t) = sin(t). - Dmitri Zaitsev
+ Dmitri Zaitsev Dấu bằng có nhiều ý nghĩa trong toán học. Một trong số đó là bất cứ khi nào bạn nhìn thấy phía bên trái, bạn có thể trao đổi nó với phía bên phải. Ví dụ 2 + 3 = 5 hoặc là a**2 + b**2 = c**2. - user712092


OK, từ kiến ​​thức nền và đọc trang Wikipedia mà bạn đã chỉ ra, có vẻ như lập trình phản ứng giống như tính toán dataflow nhưng với "kích thích" bên ngoài kích hoạt một nhóm các nút để kích hoạt và thực hiện tính toán của chúng.

Điều này khá phù hợp với thiết kế giao diện người dùng, ví dụ, trong đó chạm vào điều khiển giao diện người dùng (nói điều khiển âm lượng trên ứng dụng phát nhạc) có thể cần cập nhật các mục hiển thị khác nhau và âm lượng đầu ra âm thanh thực tế. Khi bạn sửa đổi âm lượng (một thanh trượt, giả sử) tương ứng với việc sửa đổi giá trị được liên kết với một nút trong biểu đồ được chỉ dẫn.

Các nút khác nhau có các cạnh từ nút "giá trị khối lượng" đó sẽ tự động được kích hoạt và mọi tính toán và cập nhật cần thiết sẽ tự động gợn qua ứng dụng. Ứng dụng "phản ứng" với kích thích người dùng. Chức năng lập trình phản ứng sẽ chỉ là việc thực hiện ý tưởng này trong một ngôn ngữ chức năng, hoặc nói chung trong một mô hình lập trình chức năng.

Để biết thêm về "tính toán dữ liệu", hãy tìm kiếm hai từ đó trên Wikipedia hoặc sử dụng công cụ tìm kiếm ưa thích của bạn. Ý tưởng chung là: chương trình là đồ thị được chỉ dẫn của các nút, mỗi nút thực hiện một số tính toán đơn giản. Các nút này được kết nối với nhau bằng các liên kết đồ thị cung cấp đầu ra của một số nút tới đầu vào của các nút khác.

Khi một nút kích hoạt hoặc thực hiện phép tính của nó, các nút được kết nối với các đầu ra của nó có các đầu vào tương ứng của chúng "được kích hoạt" hoặc "được đánh dấu". Bất kỳ nút nào có tất cả các đầu vào được kích hoạt / đánh dấu / có sẵn tự động kích hoạt. Biểu đồ có thể ngầm hoặc rõ ràng tùy thuộc vào chính xác cách lập trình phản ứng được thực hiện.

Các nút có thể được xem như là bắn song song, nhưng thường chúng được thực hiện theo kiểu serially hoặc với sự song song hạn chế (ví dụ, có thể có một vài luồng thực thi chúng). Một ví dụ nổi tiếng là Manchester Dataflow máy, (IIRC) đã sử dụng kiến ​​trúc dữ liệu được gắn thẻ để lên lịch thực hiện các nút trong biểu đồ thông qua một hoặc nhiều đơn vị thực thi. Dataflow tính toán là khá phù hợp với các tình huống trong đó kích hoạt tính toán không đồng bộ cho tăng tầng thác của tính toán hoạt động tốt hơn so với cố gắng để có thực hiện được điều chỉnh bởi một đồng hồ (hoặc đồng hồ).

Lập trình phản ứng nhập khẩu ý tưởng "xếp hàng thực thi" này và dường như nghĩ về chương trình theo kiểu thời trang giống như dataflow nhưng với điều kiện một số nút được nối với "thế giới bên ngoài" và các thác thực thi được kích hoạt khi các giác quan này giống như các nút thay đổi. Việc thực thi chương trình sau đó sẽ giống như một cái gì đó tương tự với một vòng cung phản xạ phức tạp. Chương trình có thể hoặc có thể không về cơ bản không ngừng giữa các kích thích hoặc có thể lắng xuống trạng thái không ổn định về cơ bản giữa các kích thích.

lập trình "không phản ứng" sẽ lập trình với một cái nhìn rất khác về luồng thực hiện và mối quan hệ với các đầu vào bên ngoài. Nó có thể là một phần chủ quan, vì mọi người có thể sẽ bị cám dỗ để nói bất cứ điều gì phản ứng với các yếu tố đầu vào bên ngoài "phản ứng" với họ. Nhưng nhìn vào tinh thần của sự vật, một chương trình thăm dò hàng đợi sự kiện tại một khoảng thời gian cố định và gửi đi bất kỳ sự kiện nào được tìm thấy cho hàm (hoặc chủ đề) ít phản ứng hơn (vì nó chỉ tham dự đầu vào của người dùng tại một khoảng thời gian cố định). Một lần nữa, đó là tinh thần của sự việc ở đây: người ta có thể hình dung việc thực hiện bỏ phiếu với một khoảng thời gian bỏ phiếu nhanh chóng vào một hệ thống ở mức rất thấp và chương trình trong một thời trang phản ứng trên đầu nó.


71
2018-06-22 17:45



OK, có một số câu trả lời hay ở trên. Tôi có nên xóa bài đăng của mình không? Nếu tôi thấy hai hoặc ba người nói rằng nó không thêm gì cả, tôi sẽ xóa nó trừ khi số lượng hữu ích của nó tăng lên. Không có điểm để rời khỏi nó ở đây trừ khi nó thêm một cái gì đó có giá trị. - Thomas Kammeyer
bạn đã đề cập đến luồng dữ liệu, để thêm một số giá trị IMHO. - Rainer Joswig
Đó là những gì QML có nghĩa là, có vẻ như;) - mlvljr
Đối với tôi, câu trả lời này là dễ hiểu nhất, đặc biệt là vì việc sử dụng các chất tương tự tự nhiên như "gợn qua ứng dụng" và "các nút giống giác quan". Tuyệt quá! - Akseli Palén
Thật không may, liên kết Manchester Dataflow Machine đã chết. - Pac0


Sau khi đọc nhiều trang về FRP tôi cuối cùng đã đi qua điều này giác ngộ bằng văn bản về FRP, nó cuối cùng đã làm cho tôi hiểu những gì FRP thực sự là tất cả về.

Tôi trích dẫn dưới đây Heinrich Apfelmus (tác giả của chuối phản ứng).

Bản chất của lập trình phản ứng chức năng là gì?

Một câu trả lời chung sẽ là "FRP là tất cả về mô tả một hệ thống trong   các điều khoản của các hàm thay đổi theo thời gian thay vì trạng thái có thể thay đổi "và   chắc chắn sẽ không sai. Đây là quan điểm ngữ nghĩa. Nhưng trong   ý kiến ​​của tôi, câu trả lời sâu sắc hơn, thỏa mãn hơn được đưa ra bởi   theo tiêu chí hoàn toàn cú pháp:

Bản chất của lập trình chức năng phản ứng là xác định hành vi động của một giá trị hoàn toàn tại thời điểm khai báo.

Ví dụ: lấy ví dụ về bộ đếm: bạn có hai nút   có nhãn "Lên" và "Xuống" có thể được sử dụng để tăng hoặc giảm   quầy. Một cách tự nhiên, trước tiên bạn sẽ chỉ định giá trị ban đầu   và sau đó thay đổi nó bất cứ khi nào một nút được nhấn; một cái gì đó như thế này:

counter := 0                               -- initial value
on buttonUp   = (counter := counter + 1)   -- change it later
on buttonDown = (counter := counter - 1)

Vấn đề là tại thời điểm khai báo, chỉ có giá trị ban đầu   cho bộ đếm được chỉ định; hành vi năng động của bộ đếm là   ẩn trong phần còn lại của văn bản chương trình. Ngược lại, chức năng   lập trình phản ứng xác định toàn bộ hành vi năng động vào thời điểm đó   tuyên bố, như thế này:

counter :: Behavior Int
counter = accumulate ($) 0
            (fmap (+1) eventUp
             `union` fmap (subtract 1) eventDown)

Bất cứ khi nào bạn muốn hiểu động lực của bộ đếm, bạn chỉ có   để xem định nghĩa của nó. Mọi thứ có thể xảy ra với nó sẽ   xuất hiện ở phía bên tay phải. Điều này rất trái ngược với   cách tiếp cận bắt buộc, nơi các khai báo tiếp theo có thể thay đổi   hành vi động của các giá trị được khai báo trước đó.

Vì vậy, trong sự hiểu biết của tôi một chương trình FRP là một tập các phương trình: enter image description here

j là rời rạc: 1,2,3,4 ...

f phụ thuộc t vì vậy điều này kết hợp khả năng mô hình hóa các tác nhân kích thích bên ngoài

tất cả trạng thái của chương trình được đóng gói trong các biến x_i

Thư viện FRP chăm sóc thời gian tiến bộ, nói cách khác, lấy j đến j+1.

Tôi giải thích các phương trình này chi tiết hơn trong điều này video.

CHỈNH SỬA:

Khoảng 2 năm sau câu trả lời ban đầu, gần đây tôi đã đi đến kết luận rằng việc triển khai FRP có một khía cạnh quan trọng khác. Họ cần (và thường làm) giải quyết một vấn đề thực tế quan trọng: hủy bỏ bộ nhớ cache.

Các phương trình cho x_i-s mô tả biểu đồ phụ thuộc. Khi một số x_i thay đổi vào thời gian j thì không phải tất cả x_i' giá trị tại j+1 cần được cập nhật, vì vậy không phải tất cả các phụ thuộc cần được tính toán lại vì một số x_i' có thể độc lập với x_i.

Hơn nữa, x_i-s thay đổi có thể được cập nhật từng bước. Ví dụ, hãy xem xét một hoạt động bản đồ f=g.map(_+1) tại Scala, nơi f và g là List của Ints. Đây f tương ứng với x_i(t_j) và g Là x_j(t_j). Bây giờ nếu tôi thêm một phần tử vào g thì sẽ lãng phí để thực hiện map hoạt động cho tất cả các phần tử trong g. Một số triển khai FRP (ví dụ: phản xạ-frp) nhằm giải quyết vấn đề này. Vấn đề này còn được gọi là tính toán gia tăng.

Nói cách khác, hành vi ( x_i-s) trong FRP có thể được coi là tính toán cache-ed. Đó là nhiệm vụ của công cụ FRP để vô hiệu hóa hiệu quả và tính toán lại các bộ nhớ cache-s ( x_i-s) nếu một số f_i-s làm thay đổi.


65
2018-01-31 03:46



Tôi đã ở đó với bạn cho đến khi bạn đi với rời rạc phương trình. Ý tưởng sáng lập của FRP là Thời gian liên tục, nơi không có "j+1Như Newton, Leibniz, và những người khác đã cho chúng ta thấy, nó thường rất tiện dụng (và "tự nhiên" theo nghĩa đen) để mô tả các chức năng này một cách khác biệt, nhưng liên tục như vậy, bằng cách sử dụng tích phân và hệ thống Nếu không, bạn đang mô tả một thuật toán xấp xỉ (và một thuật toán nghèo) thay vì chính nó. - Conal
Ngôn ngữ lập trình và hạn chế bố cục HTML layx dường như thể hiện các yếu tố của FRP. - Barry
@Conal này làm cho tôi tự hỏi làm thế nào là FRP khác với ODEs. Chúng khác nhau như thế nào? - jhegedus
@jhegedus Trong tích hợp đó (có thể đệ quy, tức là, ODE) cung cấp một trong các khối xây dựng của FRP, không phải toàn bộ. Mỗi phần tử của từ vựng FRP (bao gồm nhưng không giới hạn trong tích hợp) được giải thích chính xác theo thời gian liên tục. Giải thích đó có giúp ích gì không? - Conal


Disclaimer: câu trả lời của tôi là trong ngữ cảnh của rx.js - một thư viện 'lập trình phản ứng' cho Javascript.

Trong lập trình hàm, thay vì lặp qua từng mục của một bộ sưu tập, bạn áp dụng các hàm bậc cao hơn (HoF) cho chính bộ sưu tập đó. Vì vậy, ý tưởng đằng sau FRP là thay vì xử lý từng sự kiện riêng lẻ, hãy tạo một luồng sự kiện (được triển khai bằng một quan sát *) và áp dụng HoF cho điều đó thay thế. Bằng cách này, bạn có thể hình dung hệ thống dưới dạng đường ống dữ liệu kết nối các nhà xuất bản với người đăng ký.

Những lợi thế chính của việc sử dụng một quan sát là:
i) nó tóm tắt trạng thái khỏi mã của bạn, ví dụ: nếu bạn muốn trình xử lý sự kiện chỉ được kích hoạt cho mọi sự kiện 'n'th hoặc ngừng kích hoạt sau sự kiện' n 'đầu tiên hoặc chỉ bắt đầu kích hoạt sau lần đầu tiên' n 'sự kiện, bạn chỉ có thể sử dụng HoFs (bộ lọc, takeUntil, bỏ qua tương ứng) thay vì thiết lập, cập nhật và kiểm tra quầy.
ii) nó cải thiện vị trí mã - nếu bạn có 5 trình xử lý sự kiện khác nhau thay đổi trạng thái của một thành phần, bạn có thể hợp nhất các quan sát của chúng và xác định một trình xử lý sự kiện duy nhất trên quan sát được kết hợp. dễ dàng để lý do về những sự kiện trong toàn bộ hệ thống của bạn có thể ảnh hưởng đến một thành phần, vì tất cả đều có mặt trong một trình xử lý đơn lẻ.

  • An Observable là dual của Iterable.

Một Iterable là một chuỗi được tiêu thụ một cách lười biếng - mỗi mục được kéo bởi trình lặp bất cứ khi nào nó muốn sử dụng nó, và do đó việc liệt kê được thúc đẩy bởi người tiêu dùng.

Một quan sát được là một chuỗi được tạo ra lười biếng - mỗi mục được đẩy tới người quan sát bất cứ khi nào nó được thêm vào trình tự, và do đó điều tra được thúc đẩy bởi nhà sản xuất.


30
2018-05-26 17:10



Cảm ơn bạn rất nhiều vì định nghĩa đơn giản này của một sự quan sát và sự khác biệt của nó từ các vòng lặp. Tôi nghĩ rằng thường rất hữu ích khi so sánh một khái niệm phức tạp với khái niệm kép nổi tiếng của nó để đạt được một sự hiểu biết thực sự. - ftor
"Vì vậy, ý tưởng đằng sau FRP là thay vì xử lý từng sự kiện riêng lẻ, hãy tạo một luồng sự kiện (được thực hiện bằng một quan sát *) và áp dụng HoF cho thay vào đó." Tôi có thể nhầm lẫn nhưng tôi tin rằng đây không phải là FRP thực sự mà là một trừu tượng tốt đẹp trên mẫu thiết kế Observer cho phép các hoạt động chức năng thông qua HoF (tuyệt vời!) Trong khi vẫn có ý định sử dụng với mã lệnh bắt buộc. Thảo luận về chủ đề - lambda-the-ultimate.org/node/4982 - nqe