Duy trì nhà nước mà không cần chuyển nhượng


10

Tôi đang học lập trình chức năng và tôi gặp khó khăn trong việc hiểu cách thực hiện một số tình huống cụ thể mà không sử dụng phân công. Các vấn đề đơn giản sau đây tổng hợp khá nhiều nhầm lẫn của tôi.

Viết chương trình nhận các sự kiện về các thay đổi trong cấu trúc dữ liệu nhất định và phát ra các sự kiện khi cấu trúc dữ liệu này đạt đến một trạng thái nhất định.

Vì vậy, tôi có một bản sao của cơ sở hạ tầng mà tôi duy trì

datastructure_copy::DataStructure 

Tôi có luồng sự kiện được kích hoạt khi nó thay đổi:

datastructure_changes::Stream Change

Tôi có một hàm áp dụng thay đổi cho cấu trúc dữ liệu và trả về một bản sao mới:

apply_change::Change -> DataStructure -> DataStructure

Và tôi có một vị từ kiểm tra xem trạng thái dữ liệu đã đạt đến trạng thái mong muốn chưa.

is_ready::DataStructure ->Boolean

Nói cách khác, tôi cần một cái gì đó như 'giảm' hoạt động trên các luồng.

Tôi biết rằng một cách để thực hiện điều này là tính toán lại trạng thái mỗi khi có thay đổi, tuy nhiên điều này có vẻ không thực tế. Tôi đã chơi một chút với đơn vị Nhà nước, nhưng có vẻ như tôi muốn giải quyết một vấn đề khác.

Vì vậy, có một cách khác để làm điều đó?

Lưu ý rằng câu hỏi của tôi hoàn toàn là khái niệm và tôi không quen thuộc lắm với Haskell.


Bằng cách này hay cách khác, bạn sẽ không bao giờ thấy một 'chuyển nhượng' (lưu ý sự khác biệt giữa chuyển nhượng và ràng buộc) trong Haskell vì 'Haskell không có khái niệm về "chuyển nhượng", "trạng thái có thể thay đổi" hoặc "biến" và là "thuần túy" ngôn ngữ chức năng "' . Bang Monad nên là thứ bạn đang tìm kiếm, bạn chỉ cần học cách sử dụng nó. Nếu bạn muốn tôi có thể đưa ra một câu trả lời toàn diện hơn sau ngày hôm nay.
Francesco Gramano

Câu trả lời:


2

Tôi biết rằng một cách để thực hiện điều này là tính toán lại trạng thái mỗi khi có thay đổi, tuy nhiên điều này có vẻ không thực tế.

Nếu các thay đổi được áp dụng khi một sự kiện xảy ra không phải là phân phối, bằng cách này hay cách khác, bạn sẽ phải tính toán lại trạng thái mỗi khi một sự kiện xảy ra, vì trạng thái cuối cùng không là gì ngoài trạng thái ban đầu, cộng với các thay đổi liên tiếp. Và ngay cả khi các thay đổi có tính phân phối, bạn thường muốn chuyển đổi liên tục trạng thái sang trạng thái tiếp theo, vì bạn muốn dừng quá trình của mình nhanh nhất khi đạt đến trạng thái nhất định và vì bạn phải tính toán trạng thái tiếp theo để xác định xem cái mới là trạng thái mong muốn.

Trong lập trình chức năng, các thay đổi trạng thái thường được biểu thị bằng các lệnh gọi hàm và / hoặc tham số hàm.

Vì bạn không thể dự đoán khi nào trạng thái cuối cùng sẽ được tính toán, bạn không nên sử dụng hàm đệ quy không đuôi. Một luồng các trạng thái, trong đó mỗi trạng thái dựa trên trạng thái trước đó, có thể là một lựa chọn tốt.

Vì vậy, trong trường hợp của bạn, tôi sẽ trả lời câu hỏi theo đoạn mã sau, trong Scala:

import scala.util.Random

val initState = 0.0
def nextState(state: Double, event: Boolean): Double = if(event) state + 0.3 else state - 0.1 // give a new state
def predicate(state: Double) = state >= 1

// random booleans as events
// nb: must be a function in order to force Random.nextBoolean to be called for each  element of the stream
def events(): Stream[Boolean] = Random.nextBoolean #:: events()  

val states: Stream[Double] = initState #:: states.zip(events).map({ case (s,e) => nextState(s,e)}) // a stream of all the successive states

// stop when the state is >= 1 ;
// display all the states computed before it stopped
states takeWhile(! predicate(_)) foreach println 

Chẳng hạn, có thể đưa ra (tôi đã đơn giản hóa đầu ra):

0.0
0.3
0.2
0.5
0.8

val states: Stream[Double] = ... là dòng nơi các trạng thái liên tiếp được tính toán.

Phần tử đầu tiên của luồng này là trạng thái ban đầu của hệ thống. ziphợp nhất luồng trạng thái với luồng sự kiện thành một luồng các cặp yếu tố, mỗi cặp là một (trạng thái, sự kiện). mapbiến đổi mỗi cặp thành một giá trị duy nhất là trạng thái mới, được tính là một hàm của trạng thái cũ và sự kiện liên quan. Do đó, một trạng thái mới là trạng thái được tính toán trước đó, cộng với sự kiện liên quan "sửa đổi" trạng thái.

Về cơ bản, bạn xác định một luồng trạng thái có khả năng vô hạn, mỗi trạng thái mới là một hàm của trạng thái được tính toán cuối cùng và một sự kiện mới. Vì các luồng lười biếng trong Scala (trong số các luồng khác), chỉ có tính toán theo yêu cầu, do đó bạn không phải tính toán các trạng thái vô dụng và bạn có thể tính toán bao nhiêu trạng thái như bạn muốn.

Nếu bạn chỉ quan tâm đến trạng thái đầu tiên tôn trọng vị ngữ, hãy thay thế dòng mã cuối cùng bằng:

states find predicate get

Mà lấy:

res7: Double = 1.1

Bạn có thể cung cấp một số cái nhìn sâu sắc về dòng thực hiện phép thuật:val states: Stream[Double]...
Bobby Marinoff

Chắc chắn rồi. Hãy nhìn vào chỉnh sửa của tôi.
mgoeminne

1

Bạn nói rằng bạn có 2 chức năng:

apply_change::Change -> DataStructure -> DataStructure
is_ready::DataStructure ->Boolean

và nếu tôi hiểu bạn ngay thì is_readykhá tốn kém nên bạn không muốn làm điều đó cho mọi sự kiện thay đổi lặp đi lặp lại.

Những gì bạn cần là một hàm lấy một DataStr struct ban đầu và ngưng tụ nó thành một trạng thái đơn giản và một hàm có trạng thái cô đọng, Thay đổi và đưa ra trạng thái ngưng tụ mới.

Nói DataStr struct là một bộ ba x,y,zvà bạn đang chờ x, y và z là số nguyên tố. Trạng thái cô đọng của bạn sau đó có thể là một tập hợp trong đó x, y, z không phải là số nguyên tố. Thay đổi làm cho x nguyên tố loại bỏ x khỏi tập hợp. Thay đổi làm cho x không phải là số nguyên tố thêm x vào tập hợp (nếu không có). DataStr struct đã sẵn sàng, sau đó tập hợp trống.

Ý tưởng sẽ là việc cập nhật trạng thái cô đọng rẻ hơn rất nhiều so với việc cập nhật DataStr struct và tính toán là từ đầu.

Lưu ý: Một cách tiếp cận thậm chí tốt hơn có thể là theo dõi x, y, z ở đâu được kiểm tra là số nguyên tố và nếu chúng ở đâu. Đối với mỗi Thay đổi, bạn gắn cờ trường liên quan là không được chọn. Sau đó, khi is_ yet được gọi, bạn kiểm tra và ghi nhớ. Điều này sẽ tốt hơn nếu bạn không kiểm tra is_ yet sau mỗi Thay đổi vì x có thể thay đổi nhiều lần và bạn chỉ kiểm tra số nguyên tố một lần.

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.