2011-09-21 35 views
7

Trong chương trình đồ chơi boolean biểu hiện rất đơn giản của tôi, tôi có chức năng thẩm định sau đây:tránh đi qua rõ ràng của bảng tra cứu

eval' :: Expr -> M.Map Char Bool -> Bool 

eval' (Const c) values = c 

eval' (Var v) values = M.findWithDefault False v values 

eval' (Not x) values = not (eval' x values) 

eval' (And a b) values = eval' a values && eval' b values 

eval' (Or a b) values = eval' a values || eval' b values 

eval' (Xor a b) values = eval' a values /= eval' b values 

tôi đã tự hỏi nếu có một cách để vượt qua bảng values ngầm? Có lẽ với sự giúp đỡ của Monads?

Trả lời

4

Trong trường hợp này, không vượt qua values tại tất cả:

eval' :: Expr -> M.Map Char Bool -> Bool 
eval' e values = eval'' e 

    where 
    eval'' (Const c) = c 
    eval'' (Var v) = M.findWithDefault False v values 
    eval'' (Not x) = not (eval'' x) 
    ... 
+0

Không chỉ đơn giản này, nó còn có thể hiệu quả hơn. Tôi tin rằng nó được gọi là chuyển đổi đối số tĩnh. –

13

Monads sẽ làm việc, nhưng theo ý kiến ​​của tôi sử dụng Applicative là sạch hơn ở đây:

eval' :: Expr -> M.Map Char Bool -> Bool 
eval' (Const c) = pure c 
eval' (Var v) = M.findWithDefault False v 
eval' (Not x) = not <$> eval' x 
eval' (And a b) = (&&) <$> eval' a <*> eval' b 
eval' (Or a b) = (||) <$> eval' a <*> eval' b 
eval' (Xor a b) = (/=) <$> eval' a <*> eval' b 

này được sử dụng ví dụ ((->) r), giống như Reader đơn nguyên/applicative, nhưng nếu không có sự bao bọc Newtype.

+0

tôi sẽ đạt cho môi trường 'Reader' đơn nguyên, như acfoltzer đã làm, nhưng tôi thích nhìn sạch của giải pháp này. – pat

+0

'Sự xuất hiện mơ hồ 'Const': Nó có thể ám chỉ đến 'Main.Const' hoặc 'Control.Applicative.Const'' Oh well, tôi muốn đổi tên' Const' thành 'Val', anyway :) – fredoverflow

+1

Hoặc tôi có thể chỉ 'import Control.Applicative (thuần túy, (<*>), (<$>))' :) – fredoverflow

9

Đây là chính xác những gì Reader monad dành cho:

The Reader đơn nguyên (còn gọi là đơn nguyên Môi trường). Trình bày tính toán , có thể đọc giá trị từ môi trường được chia sẻ, chuyển các giá trị từ hàm này sang hàm khác và thực thi các phép tính phụ trong môi trường đã sửa đổi .

Như ghi chú Sjoerd, các đơn nguyên cho quyền lực hơn ở đây hơn bạn cần, nhưng bạn vẫn có thể sử dụng đơn nguyên Reader cho vấn đề này mà không cần quá nhiều như gõ do:

import qualified Data.Map as M 

import Control.Applicative ((<$>), (<*>)) 
import Control.Monad.Reader 

data Expr = Const Bool 
      | Var Char 
      | Not Expr 
      | And Expr Expr 
      | Or Expr Expr 
      | Xor Expr Expr 

Chỉ cần đặt loại môi trường của bạn làm đối số đầu tiên cho hàm tạo kiểu Reader và loại kết quả ban đầu của bạn làm loại thứ hai.

eval' :: Expr -> Reader (M.Map Char Bool) Bool 

Thay vì chỉ c như giá trị của vụ án Const, sử dụng return để nâng nó vào đơn nguyên:

eval' (Const c) = return c 

Khi bạn cần môi trường cho nhìn lên giá trị của một biến, sử dụng ask. Sử dụng do ký hiệu, bạn có thể viết các trường hợp Var như thế này:

eval' (Var v) = do values <- ask 
        return (M.findWithDefault False v values) 

Tôi nghĩ rằng nó đẹp hơn, tuy nhiên, để sử dụng fmap aka <$>:

eval' (Var v) = M.findWithDefault False v <$> ask 

Tương tự, unary not có thể được fmap PED trên kết quả của đệ quy:

eval' (Not x) = not <$> eval' x 

Fina lly, các Applicative thể hiện của Reader làm cho các trường hợp nhị phân dễ chịu:

eval' (And a b) = (&&) <$> eval' a <*> eval' b 

eval' (Or a b) = (||) <$> eval' a <*> eval' b 

eval' (Xor a b) = (/=) <$> eval' a <*> eval' b 

Sau đó, để cho nó tất cả bắt đầu, đây là một helper để tạo ra môi trường ban đầu và chạy các tính toán:

eval :: Expr -> [(Char,Bool)] -> Bool 
eval exp env = runReader (eval' exp) (M.fromList env) 
2

Bạn biết tiện ích mở rộng implicit parameters? Nó có thể khá hữu ích.

+2

Có [thảo luận về r/haskell về nó gần đây] (http://www.reddit.com/r/haskell/comments/kbzjk/what_about_the_implicitparams_haskell_language/). Sự đồng thuận dường như là nó không hữu ích, chủ yếu là bởi vì chúng bị rò rỉ thành chữ ký, do đó buộc bạn hoặc không sử dụng chữ ký kiểu nào cả, hoặc kết thúc với cùng số lượng bản mẫu mà bạn đang cố gắng loại bỏ. – hammar

+0

@hammar Điểm tốt. – fuz