2014-01-20 2 views
6

Я немного запутался в том, когда использовать или реализовывать функторы в своем коде. Я включил некоторый код ниже, который имеет две функции display_expr, cal_expr, и обе эти функции имеют одну и ту же форму, но отличаются в реализации. Будет ли это место, где я хотел бы рассмотреть создание одного функтора, который будет представлять собой основную функциональность обеих функций?OCaml - Когда использовать Функторы

type expr = 
    | Add of expr * expr 
    | Minus of expr * expr 
    | Multi of expr * expr 
    | Divide of expr * expr 
    | Value of int;; 

let rec display_expr e = 
    match e with 
    | Add (a1, a2) -> "("^display_expr a1^" + "^display_expr a2^")" 
    | Minus (m1, m2) -> "("^display_expr m1^" - "^display_expr m2^")" 
    | Multi (m1, m2) -> "("^display_expr m1^" * "^display_expr m2^")" 
    | Divide (d1, d2) -> "("^display_expr d1^"/"^display_expr d2^")" 
    | Value v -> string_of_int v;; 

let rec cal_expr e = 
    match e with 
    | Add (a1, a2) -> (cal_expr a1) + (cal_expr a2) 
    | Minus (m1, m2) -> (cal_expr m1) - (cal_expr m2) 
    | Multi (m1, m2) -> (cal_expr m1) * (cal_expr m2) 
    | Divide (d1, d2) -> (cal_expr d1)/(cal_expr d2) 
    | Value v -> v;; 

let equ = 
    Multi(Value 34, 
     Add(Value 24, 
      Divide(Value 24, 
       Minus(Value 10, Value 7) 
      ) 
     ) 
    );; 

Printf.fprintf stdout "%d = %s\n" (cal_expr equ) (display_expr equ);; 

Примечание: Я пытался писать функтор решение для приведенного выше кода, и я получил один рабочий, как только я понял, что функтор требуется общий или комбинированный тип для значений, возвращаемых display_expr и cal_expr.

Также: Я крайний новобранец OCaml, поэтому, пожалуйста, подумайте об этом в своем ответе. Спасибо.

+0

Как выглядели ваши решения functor? Мне трудно представить его, учитывая, что функторы являются по существу функциями модулей от модулей, а код, который вы указали, не содержит в нем модулей. –

ответ

4

Функцеры действительно не применяются здесь, поскольку нет модулей, но вы можете атаковать это совершенно напрямую с функциями более высокого порядка.

Ключ должен заметить, что для каждого конструктора требуется одно преобразование и, следовательно, один аргумент функции. Этот шаблон похож в некоторых отношениях на List.fold_right, который можно рассматривать как операцию, которая заменяет конструкторы списка.

type expr = 
    | Add of expr * expr 
    | Sub of expr * expr 
    | Mul of expr * expr 
    | Div of expr * expr 
    | Int of int 

let transform ~add ~sub ~mul ~div ~int expr = 
    let rec tx = function 
    | Add (x, y) -> add (tx x) (tx y) 
    | Sub (x, y) -> sub (tx x) (tx y) 
    | Mul (x, y) -> mul (tx x) (tx y) 
    | Div (x, y) -> div (tx x) (tx y) 
    | Int x -> int x in 
    tx expr 

let binary_op_str sep a b = "("^a^sep^b^")" 

let display_expr = transform 
    ~add:(binary_op_str " + ") 
    ~sub:(binary_op_str " - ") 
    ~mul:(binary_op_str " * ") 
    ~div:(binary_op_str "/") 
    ~int:string_of_int 

let cal_expr = transform 
    ~add:(+) 
    ~sub:(-) 
    ~mul:(*) 
    ~div:(/) 
    ~int:(fun x -> x) 

Возможно ли, что это предпочтительнее исходного кода, является чем-то вроде вкуса.

+0

Это для обеспечения такого чистого решения. Я очень ценю это. Я должен был действительно просмотреть ваш код, прежде чем я смог понять, что вы сделали. Хорошее решение. – G4143

+0

Если количество аргументов растет, вы можете использовать функтор, который включает в себя реализации этих переданных функций. Или создайте запись, которая включает все эти функции. Но это хорошо для проблемы. – nlucaroni

Смежные вопросы