2016-04-21 88 views
5

Bir dizi özyinelemeli işlev içeren çok uzun bir dosyam var. Özyineleme gereklidir ve kod da etkilidir. Ancak daha iyi okunabilirliğe sahip olmak için dosyayı 2 veya daha fazla dosyaya bölmek istiyorum. Ancak, OCaml'da let rec ... and ...'u nasıl bölebildiğimizi göremiyorum. OCaml'ın herhangi bir mekanizma sağladığını (örn., Arabirimleri belirtmenin bir yolu veya makefile yazmasını) biliyor mu? karşılıklı özyinelemeli fanktorlar: bir yolu vardırOCaml'de bir dizi yineleme fonksiyonunu 2 dosyaya bölmek mümkün mü?

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

Bu sorunun içeriğine biraz ışık tutabilir misiniz? Merak ediyorum: eğer bir çeşit otomatik olarak üretilmiş kodsa, neden hiç rahatsız etmiyorsun? –

+0

Otomatik olarak oluşturulmuş kod değil, bu işlevleri kendim yazdım ... – SoftTimur

+1

Bu dosya ne kadar? Elle yazılmış ise, onu bölmeyeceğim (çünkü hem verimliliği hem de okunabilirliği kaybedebilirsiniz). –

cevap

3

: gibi

çok çok uzun dosya görünebilir. OCaml ve ana fikri sağlayan this ilham verici makalede ayrı derleme hakkında this makalesine bakın.

İşte bir örnek.

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

ve 3 uygulama dosyaları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 

Ve karşılıklı özyinelemeli fanktorlar:

İşte
$ ls *.ml 
Make_moduleA.ml Make_moduleB.ml main.ml 

arayüz dosyaları içeriği olan ben 4 arayüzü mli-dosyaları oluşturduk
(* 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) 

Ve şimdi modülleri birleştirmek atalım:

(* 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 

Yapı komut dizisi:

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 

Test sonuçları:

Sadece başvuru için, funktorlar olmadan, başka bir yol var
$ build.sh && ./a.out 
A: 4 
B: 3 
A: 2 
B: 1 
-------------- 
B: 4 
A: 3 
B: 2 
A: 1 
+0

Detaylı yanıt için teşekkür ederiz ... – SoftTimur

2

. Fonksiyonları diğer parametrelerden fonksiyonlara parametre olarak eklemelisiniz. Örnek: even ve odd tanımlamalı olarak tanımlanmış, ancak even'un A ve odd modüllerinde olmak için B modülünde olmasını istiyoruz.

İlk dosyası:

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

İkinci dosyası:

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

Üçüncü dosyası:

:

(* 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") ;; 

Derleme (seçeneğini -rectypes kullanmalısınız) 10

Bunu salık vereceğime emin değilim, ama işe yarıyor.

+1

Büyük ve acımasız '-düzenlere dikkat edin! Kendi kendine uygulama (cevapta benzer bir şey kullanıldı) sağlar, bu nedenle güçlü Y birleştiricisi de derleyecektir: 'let yf = (eğlenceli xa -> f (xx) a) (eğlenceli xa -> f (xx) a) '. –

+0

Hey Anton. Haklısın. Ve ne 'rec rec x x f x' olsun? Bunu nasıl önleyebilirim? ;-) –

+0

Sadece 'fx = fx' kullanın :) Bu arada (eğer açıksa, üzgünüm), OCaml içinde 'rec' olmadan yinelemeli fonksiyonlar yaratabilirsiniz (çünkü OCaml katı pozitifliği zorlamamaktadır (örneğin Agda bunu yapar): 'tip boxed_fun = | Kutu (boxed_fun -> boxed_fun) let nonTerminating (bf: boxed_fun): boxed_fun = Kutu f ile maç bf -> f let döngü bf = nonTerminating (Kutu nonTerminating)' –