2012-02-01 21 views
6

Tôi đang mô phỏng bộ vi xử lý 4 bit. Tôi cần phải theo dõi các thanh ghi, bộ nhớ và đầu ra đang chạy (các điểm thưởng để có bộ đếm chu trình tìm nạp). Tôi đã xoay sở để làm điều này mà không có monads, nhưng nó cảm thấy lộn xộn đi qua xung quanh nhiều thứ cùng một lúc một cách rõ ràng. Ngoài ra định nghĩa chức năng là lộn xộn, dài và khó đọc.Mức độ tương tác khác nhau của trạng thái trong haskell

Tôi đã cố gắng thực hiện điều này với các monads và nó không vừa với nhau. Tôi đã cố gắng xử lý tất cả các thành phần trạng thái riêng biệt dưới dạng một kiểu duy nhất, nhưng điều đó đã để lại cho tôi vấn đề về những gì tạo ra giá trị.

State Program() -- Represents the state of the processor after a single iteration of the fetch execute cycle 

Là loại duy nhất có ý nghĩa. Nhưng tại thời điểm đó, tại sao lại bận tâm? Tôi cố gắng phá vỡ nó lên bằng cách kéo chuỗi ra loại hợp của tôi và đối xử với nó như là giá trị

State Program' String 

mà làm việc lớn, ngoại trừ một thực tế rằng tôi cần đầu ra CHẠY. Không có vấn đề gì tôi đã làm tôi không thể giữ cho cả chuỗi và nhà nước cùng một lúc.

Bây giờ tôi đang cố gắng vật lộn với máy biến áp đơn nguyên. Có vẻ như tôi phải tách ra tất cả các cấp tiểu bang khác nhau. Nhưng đầu tôi đang bùng nổ nhanh.

StateT Registers (StateT Memory (State Output)) a = 
StateT (registers -> (StateT Memory (State Output)) (a,registers)) 

StateT Registers (StateT Memory (State Output)) a = 
StateT (registers -> (Memory -> (Output -> (((a,Registers),Memory),Output)))) 

Tôi thậm chí chưa đặt trong bộ đếm FEcycle!

Câu hỏi:

  1. Am tôi đi đúng hướng?
  2. Thấy như tôi đang kéo ra biến thế đơn nguyên bây giờ, là nó có thể ngừng điều trị "chạy đầu ra" như nhà nước và chỉ cần cọ nó ra để các đơn nguyên IO? Điều đó thật tuyệt vời, thay vì giữ nó, tôi chỉ có thể in nó.
  3. Tôi nên tách tiểu bang thành bao nhiêu lớp? Tôi có thể thấy hai lớp riêng biệt, nhưng chúng phụ thuộc chặt chẽ nhau (cả bộ nhớ và thanh ghi phụ thuộc vào trạng thái của cả bộ nhớ lẫn thanh ghi). Tôi có nên giữ chúng lại với nhau như một trạng thái duy nhất hoặc tách chúng ra và ngăn xếp chúng? Cách tiếp cận nào sẽ tạo ra mã dễ đọc nhất?
+1

Có lẽ "đầu ra chạy" có thể được biểu diễn tốt nhất bằng cách sử dụng trình đơn Writer (xem http://monads.haskell.cz/html/writermonad.html)? –

Trả lời

9

Phân lớp nhiều tiểu bang trên đầu trang của nhau là một ý tưởng tồi: bạn sẽ phải soạn một loạt các lift s để nhận được ở mỗi phần của tiểu bang, chỉ được xác định bằng số lớp xuống ngăn xếp. Kinh quá! Thật vậy, thư viện mtl nói chung được thiết kế để được sử dụng, với các ngoại lệ hiếm hoi, với một biến áp đơn lẻ của từng loại "" trong một chồng.

Thay vào đó, tôi sẽ đề xuất StateT Program IO(). Giao diện cho nhà nước là như nhau và bạn có thể, như bạn đã nói, làm đầu ra trong IO chỉ đơn giản bằng cách sử dụng liftIO. Chắc chắn, loại giá trị là (), nhưng có gì sai với điều đó? Không có giá trị có liên quan nào mà bạn có thể trả về từ trình mô phỏng cấp cao nhất. Và, tất nhiên, bạn có thể có các thành phần nhỏ hơn, có thể tái sử dụng như phần trình giả lập của bạn và những thành phần đó sẽ có các loại kết quả có liên quan. (Thật vậy, get là một thành phần như vậy.) Không có gì sai khi không có giá trị trả về có ý nghĩa ở cấp cao nhất.

Theo cách thuận tiện truy cập từng phần của tiểu bang, những gì bạn đang tìm kiếm là ống kính; this Stack Overflow answer là một phần giới thiệu tuyệt vời. Chúng cho phép bạn dễ dàng truy cập và sửa đổi các phần độc lập của tiểu bang của bạn. Ví dụ: với triển khai data-lens, bạn có thể dễ dàng viết một cái gì đó như regA += 1 để tăng regA hoặc stack %= drop 2 để xóa hai phần tử đầu tiên của ngăn xếp.

Chắc chắn, nó về cơ bản chuyển mã của bạn vào đột biến bắt buộc của một tập hợp các biến toàn cầu, nhưng điều này thực sự là một lợi thế, vì đó là chính xác mô hình CPU bạn đang thi đua có trụ sở tại. Và với gói data-lens-template , bạn có thể lấy được những thấu kính này từ một định nghĩa bản ghi trong một dòng.

+0

Thật tuyệt vời. Đã đến lúc tìm hiểu mẫu haskell. Có dễ dàng tìm các công cụ sao lưu định nghĩa chức năng như regA + = 1 không? Nguyên nhân trong khi đó là tốt đẹp và dễ đọc và rõ ràng nhất thể hiện ý định (rất bắt buộc), nó trông giống như mã chức năng. – TheIronKnuckle

+1

Bạn không cần phải học mẫu Haskell để sử dụng mẫu dữ liệu-ống kính; chỉ cần gắn '{- # LANGUAGE TemplateHaskell # -}' vào đầu tệp của bạn và 'makeLenses ['' Program] ở đâu đó bên dưới định nghĩa của' Program'. Theo định nghĩa của '(+ =)', chắc chắn; chúng chỉ là trình bao bọc đơn giản của API lõi ống kính cho 'StateT'; [nguồn] (http://hackage.haskell.org/packages/archive/data-lens/2.0.2/doc/html/src/Data-Lens-Strict.html) được liên kết từ tài liệu Hackage. – ehird

+2

Thưa quý vị - các tiểu bang có phân lớp nhỏ là một ý tưởng tồi - _không cần bạn thực sự muốn họ_. Chúng cho phép phân vùng trạng thái, vì vậy bạn có thể giới hạn một số khách hàng chỉ đọc quyền truy cập vào một lớp cụ thể của trạng thái chứ không phải truy cập đọc-ghi, điều này được sử dụng cho mã "có ý thức bảo mật". Nếu không thì câu trả lời tốt. –

2

Một cách đơn giản để làm điều này sẽ tạo ra một kiểu dữ liệu đại diện cho thanh ghi và bộ nhớ:

data Register = ... 
data Memory = ... 
data Machine = Machine [Register] Memory 

Sau đó, có một số chức năng mà cập nhật thanh ghi/bộ nhớ. Bây giờ sử dụng loại này cho tiểu bang của bạn và đầu ra cho loại hình của bạn:

type Simulation = State Machine Output 

Bây giờ mỗi hoạt động có thể ở dạng:

operation previous = do machine <- get 
         (result, newMachine) <- operate on machine 
         put newMachine 
         return result 

Đây previous là sản phẩm trước của máy. Bạn có thể kết hợp nó vào kết quả là tốt.

Vì vậy, loại Machine thể hiện trạng thái của máy; bạn đang luồng đầu ra của các hoạt động trước đó thông qua nó.

Một cách tinh vi hơn là sử dụng state threads (Control.Monad.ST). Chúng cho phép bạn sử dụng các tham chiếu có thể thay đổi và các mảng bên trong một hàm trong khi được đảm bảo độ tinh khiết ở bên ngoài.

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