2009-06-24 35 views
12

Tôi có một chức năng:Làm cách nào để 'nhận' thực sự/nhận/trạng thái ban đầu trong Haskell?

test :: String -> State String String 
test x = 
    get >>= \test -> 
    let test' = x ++ test in 
    put test' >> 
    get >>= \test2 -> put (test2 ++ x) >> 
    return "test" 

tôi khá nhiều có thể hiểu được những gì diễn ra trên khắp chức năng này, và tôi bắt đầu nhận được hang của monads. Những gì tôi không hiểu là làm thế nào, khi tôi chạy điều này:

runState (test "testy") "testtest" 

chức năng 'get' trong 'test' bằng cách nào đó nhận trạng thái ban đầu "testtest". Ai đó có thể phá vỡ điều này và giải thích nó cho tôi?

Tôi đánh giá cao mọi phản hồi!

Trả lời

18

Tôi ban đầu sẽ đăng bài này làm nhận xét nhưng đã quyết định giải thích thêm một chút.

Nói đúng, get không "lấy" một đối số. Tôi nghĩ rất nhiều những gì đang diễn ra được che giấu bởi những gì bạn không nhìn thấy - các định nghĩa cá thể của nhà nước.

get thực sự là một phương pháp của lớp MonadState. Các đơn nguyên nhà nước là một thể hiện của MonadState, cung cấp các định nghĩa sau đây của get:

get = State $ \s -> (s,s) 

Nói cách khác, get chỉ trả về một đơn nguyên nhà nước rất cơ bản (ghi nhớ rằng một đơn nguyên có thể được coi như là một "wrapper" cho một tính toán), trong đó bất kỳ đầu vào s vào tính toán sẽ trả về một cặp s là kết quả.

Điều tiếp theo chúng ta cần phải nhìn vào là >>=, mà Nhà nước định nghĩa thusly:

m >>= k = State $ \s -> let 
    (a, s') = runState m s 
    in runState (k a) s' 

Vì vậy, >>= sẽ mang lại một tính toán mới, trong đó sẽ không được tính cho đến khi nó nhận được một trạng thái ban đầu (điều này đúng với tất cả các tính toán của Nhà nước khi chúng ở dạng "bọc" của chúng). Kết quả của tính toán mới này đạt được bằng cách áp dụng bất cứ điều gì ở phía bên phải của >>= đến kết quả của việc chạy tính toán ở bên trái.(Đó là một câu khá khó hiểu có thể yêu cầu thêm một hoặc hai lần đọc.)

Tôi thấy nó khá hữu ích để "desugar" mọi thứ đang diễn ra. Làm như vậy sẽ mất nhiều đánh máy hơn, nhưng nên trả lời câu hỏi của bạn (trong đó get đang nhận được từ) rất rõ ràng. Lưu ý rằng những điều sau đây cần được xem xét psuedocode ...

test x = 
    State $ \s -> let 
     (a,s') = runState (State (\s -> (s,s))) s --substituting above defn. of 'get' 
     in runState (rightSide a) s' 
     where 
      rightSide test = 
      let test' = x ++ test in 
      State $ \s2 -> let 
      (a2, s2') = runState (State $ \_ -> ((), test')) s2 -- defn. of 'put' 
      in runState (rightSide2 a2) s2' 
      rightSide2 _ = 
      -- etc... 

Điều đó sẽ làm cho nó rõ ràng rằng kết quả cuối cùng của chức năng của chúng tôi là một tính toán nhà nước mới sẽ cần một giá trị ban đầu (s) để làm cho phần còn lại của công cụ xảy ra. Bạn đã cung cấp s làm "testtest" với cuộc gọi runState của mình. Nếu bạn thay thế "testtest" cho s trong mã giả ở trên, bạn sẽ thấy rằng điều đầu tiên xảy ra là chúng tôi chạy get với "testtest" làm 'trạng thái ban đầu'. Điều này mang lại ("testtest", "testtest") và cứ tiếp tục như vậy.

Vì vậy, đó là nơi get nhận trạng thái ban đầu của bạn "kiểm tra". Hi vọng điêu nay co ich!

+2

Tôi không thể nghĩ ra một từ nào tốt hơn khi tôi nói "lấy" một cuộc tranh luận. Cảm ơn lời giải thích rất chi tiết này. – Rayne

5

Nó có thể giúp bạn xem xét sâu hơn về phương thức khởi tạo của loại State và cách runState sử dụng nó. Trong GHCi:

Prelude Control.Monad.State> :i State 
newtype State s a = State {runState :: s -> (a, s)} 
Prelude Control.Monad.State> :t runState 
runState :: State s a -> s -> (a, s) 

State có hai đối số: loại trạng thái và loại trả về. Nó được thực hiện như một hàm lấy trạng thái ban đầu và sinh ra một giá trị trả về và trạng thái mới.

runState có chức năng như vậy, đầu vào ban đầu và (nhiều khả năng) chỉ áp dụng một cho hàm kia để truy xuất cặp (kết quả, trạng thái).

Chức năng test của bạn là thành phần lớn của State chức năng kiểu, mỗi chức năng nhập vào trạng thái và cho kết quả (kết quả, trạng thái), cắm vào nhau theo cách có ý nghĩa với chương trình của bạn. Tất cả runState đều cung cấp cho họ điểm xuất phát trạng thái.

Trong ngữ cảnh này, get chỉ đơn giản là hàm lấy trạng thái làm đầu vào và trả về kết quả (kết quả, trạng thái) sao cho kết quả là trạng thái đầu vào và trạng thái không thay đổi (trạng thái đầu ra là đầu vào tiểu bang). Nói cách khác, get s = (s, s)

+1

Clickedy click click! Chơi lô tô. Cảm ơn rất nhiều, bạn đã giải thích làm cho nó nhấp vào. Tôi không bao giờ nhận ra rằng thực sự đã 'lấy' một đối số. Hehe. Cảm ơn. – Rayne

1

Đi qua chương 8 ("Hàm phân tích cú pháp") của Graham Hutton Programming in Haskell nhiều lần cho đến khi tôi hiểu chính xác nó, tiếp theo là đi theo hướng dẫn All About Monads, thực hiện nhấp chuột này cho tôi.

Vấn đề với monads là chúng rất hữu ích đối với một số điều mà những người trong chúng ta đến từ nền lập trình thông thường thấy khá khác nhau. Phải mất một thời gian để đánh giá cao rằng dòng điều khiển và trạng thái xử lý không chỉ đủ tương tự để chúng có thể được xử lý bởi cùng một cơ chế, mà là khi bạn lùi lại đủ xa, điều tương tự.

Một biểu hiện đến khi tôi xem xét cấu trúc điều khiển trong C (forwhile, v.v.), và tôi nhận ra rằng đến nay cấu trúc điều khiển phổ biến nhất chỉ đơn giản là đặt một câu lệnh trước câu lệnh kia. Phải mất một năm nghiên cứu Haskell trước khi tôi nhận ra rằng thậm chí là một cấu trúc điều khiển.

+0

Cảm ơn câu trả lời. May mắn thay, hầu hết các kinh nghiệm lập trình của tôi thực sự nằm trong lập trình chức năng, do đó, nó không phải là khó khăn về tôi! : D – Rayne

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