2017-03-11 6 views
2

Ocaml을 사용하여 모의 통역사를 구현하고 있습니다. 모의 기능 중 하나는 패턴 일치를 사용하여 구성된 표현을 평가하는 것입니다. 문제는 간단하고 확실하게 고칠 수 있지만, 좀 더 우아한 방법을 찾으려고 노력하고 있습니다. 그래도 여전히 관심이 있다면 읽어보십시오!Ocaml에서 try/with 문을 사용하여 유형을 확인 하시겠습니까?

내 사용자 정의 형식의 코드 조각은 다음과 같습니다

물론
type value = 
    | Val_Int of int 
    | Val_Bool of bool 

type expr = 
    | Id of string 
    | Int of int 
    | Bool of bool 
    | Plus of expr * expr 
    | Equal of expr * expr 

, 내가 (string * value) list -> expr -> value의 형태로 이러한 식을 평가하는 기능을 가지고, 함수의 코드 조각은 다음과 같습니다

여기
(* Ignore this helper function if you'd like *) 
let rec lookup env x = match env with 
    | [] -> raise (DeclarationError "Declaration Error") 
    | (y,v)::env_t -> if x = y then v else lookup env_t x 
;; 

(* The evaluation function *) 
let rec eval_expr env e = match e with 
    | Id(x) -> lookup env x 
    | Int(x) -> Val_Int(x) 
    | Bool(x) -> Val_Bool(x) 
    | Plus(x,y) -> (try 
        let Val_Int n1 = eval_expr env x in 
        let Val_Int n2 = eval_expr env y in 
        Val_Int (n1 + n2) 
        with _ -> raise (TypeError "Type Error Plus")) 
    | Equal(x,y) -> (try 
        let value n1 = eval_expr env x in 
        let value n2 = eval_expr env y in 
        Val_Bool (n1 = n2) 
        with _ -> raise (TypeError "Type Error Equal")) 
;; 

나는 어떤 원시 오류 유형을 잡으려고 try/with 문을 사용하여 내 자신의 오류 TypeError을 던지는거야. 1 + true과 같은 잘못된 유형 연산에서 내 변수를 보호하기 위해 Val_Int n1과 같은 다형 변수를 사용하고 있는데, 이는 TypeError을 반환해야합니다.

문제는 Equals 개입니다. 두 인수가 동일한 유형의 경우 Equals 만 평가되어야한다 (모두 Val_Int 또는 둘 모두 Val_Bool 즉), 및 Equals(Val_Int(0), Val_Bool(false)) 같은이 전달되는 경우 TypeError을 던져. Plus

을, 나는 명시 적으로 내 유형을 정의 할 수 있어요 Val_Int과 같으므로 Plus(Val_Int(0), Val_Bool(false))과 같은 것으로 match failure을 던지고 try/with 문에 걸리 겠지만 Val_Int 또는 Val_Bool 일 수있는 Equals 일 수는 없습니다. 즉, Equals(Val_Int(0), Val_Bool(false))과 같은 것은 오류를 던지는 대신 Val_Bool(false)을 반환합니다.

|Equal(x,y)->(match (eval_expr env x,eval_expr env y) with 
      |(Val_Int(a),Val_Int(b)) -> Val_Bool(a = b) 
      |(Val_Bool(a),Val_Bool(b)) -> Val_Bool(a = b) 
      |(_,_) -> raise (TypeError "TypeError Equals")) 

하지만 난이 일을 더 우아한 방법을 찾으려고 노력 해요 : 내가 좋아하는 뭔가를 한 경우처럼 나는, try/with 대신 match/with 문을 사용하면 내가이 문제를 해결할 수

한 가지 방법이다. 어떤 제안?

+0

를 구성하여 artithmetical (RESP, 부울) 평가자을 수정할 수 있습니다, 'match'는 적절한 해결책입니다. – Bergi

+0

사실 'match'는 적절한 해결책이지만 그는 또 다른 방법을 원했습니다. '모의'는'더미 '와 같은가요? – Lhooq

답변

0

글쎄, 도전을 받아들입니다.

우선 표현식에 IntBool이 있으므로 유형 value이 필요하지 않습니다. 그러나 그렇게 그것의 유형이 value이 유형을 유지할 수 있고, 그건 내가 할 일이야 :

type expr = 
    | Id of string 
    | Int of int 
    | Bool of bool 
    | Plus of expr * expr 
    | Equal of expr * expr 

type ty = Int | Bool 

(* I used the type expr but we know that we will only put Int or Bool in it *) 
type value = { e : expr; t : ty } 

exception DeclarationError of string 
exception TypeError of string 

module SMap = Map.Make (struct 
    type t = string  
    let compare = compare 
end) 

let rec lookup env x = 
    try SMap.find x env 
    with Not_found ->raise (DeclarationError "Declaration Error") 
;; 

let rec eval_expr env e = match e with 
    | Id x -> lookup env x 
    | Int _ -> {e; t = Int} 
    | Bool _ -> {e; t = Bool} 
    | Plus (e1, e2) -> 
    let v1 = eval_expr env e1 and 
     v2 = eval_expr env e2 in 
    begin 
     match v1.e, v2.e with 
     | Int e1, Int e2 -> {e = Int (e1 + e2); t = Int} 
     | _ -> raise (TypeError "Type Error Plus") 
    end 
    | Equal (e1, e2) -> 
    let v1 = eval_expr env e1 and 
     v2 = eval_expr env e2 in 
    if v1.t = v2.t then {e = Bool (v1.e = v2.e); t = Bool} 
    else raise (TypeError "Type Error Equal") 

예,이 경우 유형은, Equal 구조의 경우, 한 번 사용되지만 더 복잡한 시스템을 원한다면 더 많은 유형을 추가해야하며 잘 작동 할 것입니다.

Plus 구조에서 값이 필요하기 때문에 유형이 아닌 표현식과 일치한다는 것을 알 수 있습니다.

고객님의 lookup 기능에 대해 List이 아닌 잠시 시간을내어 Map을 사용했습니다.

2

잡기 Match_failure은 매우 형편이 좋지 않으므로 더 깨끗한 방법을 찾아보십시오.

다음은 값의 일치를 제외하는 간단한 방법입니다. 어설 션을 사용자 자신의 오류 메시지로 바꿉니다.

type value = 
    | Val_Int of int 
    | Val_Bool of bool 

type expr = 
    | Id of string 
    | Int of int 
    | Bool of bool 
    | Plus of expr * expr 
    | Equal of expr * expr 

let rec lookup env x = match env with 
    | [] -> assert false 
    | (y,v)::env_t -> if x = y then v else lookup env_t x 

let int_of_val = function 
    | Val_Int n -> n 
    | _ -> assert false 

let val_equal a b = 
    match a, b with 
    | Val_Int x, Val_Int y -> x = y 
    | Val_Bool x, Val_Bool y -> x = y 
    | _, _ -> assert false 

let rec eval_expr env = function 
    | Id name -> lookup env name 
    | Int n -> Val_Int n 
    | Bool b -> Val_Bool b 
    | Plus (x, y) -> 
    Val_Int (int_of_val (eval_expr env x) + 
      int_of_val (eval_expr env y)) 
    | Equal (x,y) -> 
    Val_Bool (val_equal (eval_expr env x) (eval_expr env y)) 
+0

나는이 솔루션을 좋아한다. ;-) – Lhooq

0

는 상호 재귀 함수를 사용하는 것하고 또 다른 방법 : 산술 evalution 다른 하나는 부울 평가 하나. 또한, 귀하의 EXPR 유형이 될 것입니다 :

type expr = Arith of aexpr 
      | Boolean of bexpr 

and aexpr = Int of int 
      | Plus of aexpr * aexpr 

and bexpr = Bool of bool 
      | EqualArith of aexpr * aexpr 
      | EqualBool of bexpr * bexpr 

이 방법 Plus 단지 건설하여 산술 식을 받아 들일 수 있습니다. 비슷하게 부울 평등을위한 하나의 생성자와 산술적 평등을위한 하나의 생성자가 있습니다. 또한 두 가지로 평가 기능을 분리해야한다 :

let rec eval_expr env = function 
    | Arith e -> Val_int (eval_aexpr e) 
    | Boolean b -> Val_bool (eval_bexpr b) 

and eval_aexpr = function 
    | Int i -> i 
    | Plus i,j -> (eval_aexpr i) + (eval_aexpr j) 

and eval_bexpr = function 
    | Bool b  -> b 
    | EqualArith (e1,e2) -> 
     let v_e1 = eval_aexpr e1 and v_e2 = eval_aexpr e2 in 
     v_e1 = v_e2 
    | EqualBool (e1,e2) -> 
     let v_e1 = eval_bexpr e1 and v_e2 = eval_bexpr e2 in 
     v_e1 = v_e2 

코딩이 방법을 사용하면 산술 (RESP 부울)를 추가 할 때 당신은 단지 예