2016-04-21 13 views
5

Tôi có một tệp rất dài chứa một tập hợp các hàm đệ quy. Việc đệ quy là cần thiết và mã cũng có hiệu quả. Nhưng tôi chỉ muốn chia tệp thành 2 hoặc nhiều tệp để có khả năng đọc tốt hơn. Nhưng tôi không thấy cách chúng tôi có thể tách let rec ... and ... trong OCaml ...Có thể chia một tập hợp các hàm đệ quy thành 2 tệp trong OCaml không?

Có ai biết nếu OCaml cung cấp bất kỳ cơ chế nào (ví dụ: cách xác định giao diện hoặc viết makefile) để làm như vậy?

Các tập tin rất rất dài có thể trông giống như:

let rec f1() = 
    ... 
    f7(); (* f7 is called only once by all the functions in the file *) 
    f2(); 
    ... 
and f2() = 
    ... 
    f1(); 
... 
and f7() = 
    ... 
    f1(); 
    ... 
+0

Bạn có thể làm sáng tỏ một chút về bối cảnh của vấn đề này không? Tôi chỉ tò mò: nếu đó là một số loại mã được tạo tự động, thì tại sao lại bận tâm? –

+0

Nó không phải là mã được tạo tự động, tôi đã tự viết các chức năng này ... – SoftTimur

+1

Tệp đó dài bao lâu? Nếu nó được viết bằng tay, tôi sẽ không chia nó (vì bạn có thể mất cả hiệu quả và khả năng đọc). –

Trả lời

3

Có một cách: functors lẫn nhau đệ quy. Xem this bài viết tuyệt vời về biên soạn riêng trong OCaml và this bài viết đầy cảm hứng cung cấp ý tưởng chính.

Đây là một ví dụ. Tôi đã tạo giao diện 4 MLI-files:

$ ls *.mli 
Make_moduleA.mli Make_moduleB.mli ModuleA.mli ModuleB.mli 

và 3 thực hiện tập tin:

$ ls *.ml 
Make_moduleA.ml Make_moduleB.ml main.ml 

Dưới đây là nội dung các tập tin giao diện:

(* ModuleA.mli *) 
module type ModuleA = sig 
    val fa : int -> unit 
end 

(* ModuleB.mli *) 
module type ModuleB = sig 
    val fb : int -> unit 
end 

(* Make_moduleA.mli *) 
open ModuleA 
open ModuleB 
module type Make_moduleA_sig = 
    functor (Mb : ModuleB) -> 
    sig 
     val fa : int -> unit 
    end 
module Make_moduleA : Make_moduleA_sig 

(* Make_moduleB.mli *) 
open ModuleA 
open ModuleB 
module type Make_moduleB_sig = 
    functor (Ma : ModuleA) -> 
    sig 
     val fb : int -> unit 
    end 
module Make_moduleB : Make_moduleB_sig 

Và đôi bên cùng có functors đệ quy:

(* Make_moduleA.ml *) 
open ModuleA 
open ModuleB  
module type Make_moduleA_sig = 
    functor (Mb : ModuleB) -> 
    sig 
     val fa : int -> unit 
    end 
module Make_moduleA_impl = 
    functor (Mb : ModuleB) -> 
    struct 
     let rec fa (n : int) = 
     if n > 0 then 
      (Printf.printf "A: %d\n" n; 
      Mb.fb (n - 1)) 
    end 
module Make_moduleA = (Make_moduleA_impl : Make_moduleA_sig) 

(* Make_moduleB.ml *) 
open ModuleA 
open ModuleB  
module type Make_moduleB_sig = 
    functor (Ma : ModuleA) -> 
    sig 
     val fb : int -> unit 
    end  
module Make_moduleB_impl = 
    functor (Ma : ModuleA) -> 
    struct 
     let rec fb (n : int) = 
     if n > 0 then 
      (Printf.printf "B: %d\n" n; 
      Ma.fa (n - 1)) 
    end  
module Make_moduleB = (Make_moduleB_impl : Make_moduleB_sig) 

Và bây giờ chúng ta hãy kết hợp các module:

(* main.ml *) 
open ModuleA 
open ModuleB 
open Make_moduleA 
open Make_moduleB 

module rec MAimpl : ModuleA = Make_moduleA(MBimpl) 
and MBimpl : ModuleB = Make_moduleB(MAimpl) 

let _ =  (* just a small test *) 
    MAimpl.fa 4; 
    print_endline "--------------"; 
    MBimpl.fb 4 

Build lệnh trình tự:

ocamlc -c ModuleA.mli 
ocamlc -c ModuleB.mli 
ocamlc -c Make_moduleA.mli 
ocamlc -c Make_moduleB.mli 

ocamlc -c Make_moduleA.ml 
ocamlc -c Make_moduleB.ml 
ocamlc -c main.ml 

ocamlc Make_moduleA.cmo Make_moduleB.cmo main.cmo 

Kết quả thử nghiệm:

$ build.sh && ./a.out 
A: 4 
B: 3 
A: 2 
B: 1 
-------------- 
B: 4 
A: 3 
B: 2 
A: 1 
+0

Cảm ơn bạn đã trả lời chi tiết ... – SoftTimur

2

Chỉ cần để tham khảo, có một cách khác, không functors. Bạn phải đặt các hàm từ mô đun khác làm tham số của các hàm của bạn. Ví dụ: chúng tôi có evenodd được xác định đệ quy, nhưng chúng tôi muốn even nằm trong mô-đun Aodd để ở trong mô-đun B.

tập tin đầu tiên:

(* A.ml *) 
let rec even odd n = 
    if n=0 
    then true 
    else 
    if n=1 
    then false 
    else (odd even) (n-1) ;; 

Thứ hai file:

(* B.ml *) 
let rec odd even n = 
if n=1 
    then true 
    else 
    if n=0 
    then false 
    else even odd (n-1) ;; 

Thứ ba file:

(* C.ml *) 
let even = A.even B.odd 
and odd = B.odd A.even ;; 
print_endline (if even 5 then "TRUE" else "FALSE") ;; 
print_endline (if odd 5 then "TRUE" else "FALSE") ;; 

Compilation (bạn phải sử dụng tùy chọn -rectypes):

ocamlc -rectypes -c A.ml 
ocamlc -rectypes -c B.ml 
ocamlc -rectypes -c C.ml 
ocamlc -rectypes A.cmo B.cmo C.cmo 

Tôi không chắc chắn tôi sẽ recommand rằng, nhưng nó hoạt động.

+1

Hãy cẩn thận với những 'tuyệt vời và tàn nhẫn' -rectypes'! Nó cho phép tự áp dụng (một điều tương tự đã được sử dụng trong câu trả lời), do đó, bộ kết hợp Y mạnh mẽ sẽ biên dịch quá: 'let yf = (fun xa -> f (xx) a) (vui vẻ xa -> f (xx) a) '. –

+0

Xin chào Anton. Bạn đúng rồi. Và những gì về 'let rec f x = f x'? Làm thế nào tôi có thể tránh nó? ;-) –

+0

Chỉ cần sử dụng 'let fx = fx' :) Ngẫu nhiên (xin lỗi, nếu nó hiển nhiên), bạn có thể tạo các hàm đệ quy trong OCaml mà không cần' let rec' (vì OCaml không thực thi nghiêm chỉnh độ nhạy (ví dụ Agda thực hiện điều đó): 'loại boxed_fun = | Hộp (boxed_fun -> boxed_fun) hãy nonTerminating (bf: boxed_fun): boxed_fun = trận đấu bf với Hộp f -> f bf let loop = nonTerminating (Hộp nonTerminating) ' –

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