2011-08-10 35 views
8

Tôi đang viết một số mã (bộ lấy mẫu MCMC của Metropolis-Hastings) sẽ sử dụng trình tạo số ngẫu nhiên và sửa đổi một mảng và các cấu trúc khác có khả năng dựa trên điều này.Cách tốt nhất để theo dõi một số tham chiếu giữa các hàm trong ST monad?

Ý tưởng ban đầu của tôi là sử dụng đơn ST, để tôi có thể sử dụng các mảng ST và gói mersenne-random-pure64, giữ bộ tạo PureMT như một phần của trạng thái.

Tuy nhiên tôi muốn có thể tách một số công việc thành các hàm trợ giúp riêng biệt (ví dụ: lấy mẫu một số nguyên ngẫu nhiên trong một phạm vi nhất định, để cập nhật cấu trúc mảng và những thứ phức tạp hơn). Để làm điều này, tôi nghĩ rằng tôi sẽ cần phải chuyển các tham chiếu đến gen PureMT và mảng tới tất cả các hàm, mà có thể nhanh chóng trở nên rất xấu nếu tôi cần lưu trữ nhiều trạng thái hơn. Bản năng của tôi là nhóm tất cả trạng thái thành một kiểu dữ liệu duy nhất mà tôi có thể truy cập ở bất cứ nơi nào, vì tôi sẽ sử dụng đơn vị trạng thái bằng cách xác định một kiểu dữ liệu mới, nhưng tôi không biết liệu có thể với ST đó hay không. monad, hoặc đúng cách để đi về nó.

Có mẫu nào hay để làm việc này không? Tôi muốn giữ mọi thứ càng chung càng tốt bởi vì tôi có thể sẽ cần thêm trạng thái phụ và xây dựng thêm mã đơn điệu xung quanh các phần hiện có.

Tôi đã thử tìm kiếm các ví dụ về mã mon đơn ST nhưng nó dường như không được đề cập trong Real World Haskell và các ví dụ wiki haskell rất ngắn và đơn giản.

cảm ơn!

Trả lời

10

Bản năng của tôi là nhóm tất cả trạng thái thành một kiểu dữ liệu duy nhất mà tôi có thể truy cập ở bất cứ nơi nào, bằng cách sử dụng đơn vị trạng thái bằng cách xác định kiểu dữ liệu mới, nhưng tôi không biết ST monad, hoặc đúng cách để đi về nó.

Có mẫu nào hay để làm việc này không? Tôi muốn giữ mọi thứ càng chung càng tốt bởi vì tôi có thể sẽ cần thêm trạng thái phụ và xây dựng thêm mã đơn điệu xung quanh các phần hiện có.

Điểm mấu chốt để nhận ra đây là nó hoàn toàn không liên quan mà bạn đang sử dụng ST. Tài liệu tham khảo ST là các giá trị thông thường mà bạn cần truy cập ở nhiều nơi khác nhau, nhưng bạn không thực sự muốn thay đổi chúng! Tính đột biến xảy ra ở ST, nhưng giá trị STRef và về cơ bản không có nội dung chỉ đọc. Chúng là các tên trỏ đến dữ liệu có thể thay đổi.

Tất nhiên, quyền truy cập chỉ đọc vào môi trường xung quanh là những gì mà đơn vị Reader dành cho. Việc chuyển các tham chiếu xấu đến tất cả các hàm là chính xác những gì nó đang làm cho bạn, nhưng vì bạn đã có trong ST, bạn có thể chỉ gắn nó vào như một biến thể đơn nguyên. Như một ví dụ đơn giản, bạn có thể làm một cái gì đó như thế này:

newtype STEnv s e a = STEnv (ReaderT e (ST s) a) 
    deriving (Functor, Applicative, Monad) 

runEnv :: STEnv s e a -> ST s e -> ST s a 
runEnv (STEnv r) e = runReaderT r =<< e 

readSTEnv :: (e -> STRef s a) -> STEnv s e a 
readSTEnv f = STEnv $ lift . readSTRef . f =<< ask 

writeSTEnv :: (e -> STRef s a) -> a -> STEnv s e() 
writeSTEnv f x = STEnv $ lift . flip writeSTRef x . f =<< ask 

Đối với tính tổng quát hơn, bạn có thể abstract over the details of the reference types, và làm cho nó thành một "môi trường với sự tham khảo có thể thay đổi" chung đơn nguyên.

+0

Điều này nghe có vẻ như một ý tưởng hay, tôi quên rằng các tham chiếu không thực sự thay đổi. Cám ơn vì cái này! Tôi cũng tự hỏi về việc có thể sử dụng ST monad từ bên trong chính nó để tách các tính toán trạng thái độc lập đang chạy trong môi trường mức cao hơn mà họ không cần truy cập, với trạng thái cục bộ ẩn của riêng chúng. Tôi có cần phải sử dụng biến áp ST monad cho điều này, hoặc bạn có thể chỉ cần sử dụng let a = runST $ .... bên trong ST monad? Nếu bạn có thể (mà không sử dụng máy biến áp) điều gì sẽ xảy ra nếu bạn cố gắng dereference một ref từ các đơn nguyên kèm theo (giả sử họ sẽ có trong phạm vi)? – Tom

+0

@Tom: Không có biến áp nào cho 'ST' - như' IO', nó luôn ở dưới cùng của bất kỳ ngăn xếp nào. Hơn nữa, bất kỳ hai phép tính 'ST' hoàn chỉnh nào - tức là, những thứ xảy ra bên trong một cuộc gọi tới' runST' - hoàn toàn tách biệt khỏi trạng thái của nhau, và bất kỳ nỗ lực trộn nào chúng sẽ đưa ra một lỗi kiểu. Bạn có thể vượt qua các giá trị mờ đục xung quanh, giống như bạn có thể truyền 'IORef' '' '' '' '' 'bên trong' ST', nhưng bạn không thể sử dụng * chúng nhiều hơn bạn có thể sử dụng 'IORef'. Bạn có thể sử dụng 'runST' giống như bình thường, mặc dù; kết quả của điều đó là một giá trị thuần túy, như mọi khi. –

+0

@Tom: Ngoài ra, nếu bạn sử dụng 'ReaderT' như trong ví dụ của tôi, bạn có thể thực hiện một số loại hoạt động của khung mà không gặp quá nhiều rắc rối - nhân bản môi trường hiện tại để chạy trong một' ST' riêng biệt bên trong, chạy một cái gì đó trong cùng 'ST' với môi trường khác, & c. –

5

Bạn có thể sử dụng đơn vị ST giống như đơn nguyên IO, nhớ rằng bạn chỉ nhận được mảng và refs và không có goodies IO khác. Cũng giống như IO, bạn có thể xếp lớp StateT lên nó nếu bạn muốn chỉ rõ một số trạng thái một cách minh bạch thông qua tính toán của bạn.

+0

Ồ tôi không nghĩ về điều đó, tôi nghĩ điều đó sẽ giải quyết được vấn đề của tôi. – Tom

Các vấn đề liên quan