2012-04-04 11 views
7

Próbuję zaimplementować rodzajowe funkcji timera w SML, który zabierze jako wejście funkcji arbitralnej liczbę operandów i typ „R Wróć i zwraca funkcję z:generic zegar funkcja high-order w SML

  • tej samej budowie i rodzaje parametrów wejściowych i
  • typu powrotu float * 'r którym pływak będzie metryki czasu spędzonego w funkcji (na przykład donosi Sys.time())

Problem polega na tym, że nie mogę go wdrożyć w taki sposób, aby mógł obsłużyć funkcje dowolnej arii. Na przykład. następujący kod:

 
let timer f =    
    let timerf x y =         
     let t0 = Sys.time()           
     in let result = f x y             
     in let diff = Sys.time() -. t0          
     in diff, result          
    in timerf  

działa tylko z funkcjami wejścia liczbę operandów 2. To nie jest dla mnie oczywiste, w jaki sposób uogólnić go obsługiwać funkcje dowolnego liczbę operandów. Miałem nadzieję, że aplikacje o częściowej funkcji w jakiś magiczny sposób rozwiążą zagadkę, ale nie mogę jej uruchomić.

Odpowiedz

9

Rozumiem twój zamiar robienia funkcji timera z arbitralną arnością. Ale nie można tego zrobić w łatwy sposób w OCaml.

Ponadto, funkcja timera tylko jeden param wystarcza do zastosowania w praktyce:

let timer f x = 
    let t0 = Sys.time()           
    in let result = f x            
    in let diff = Sys.time() -. t0          
    in diff, result 

Ponieważ każdy funkcja g z dowolnym liczbę operandów może zostać przekazany do timer łatwo przez:

let diff, result = timer (fun() -> g x1 x2 x3 ... xN)() 

lub lepiej, używając częściowego zastosowania (zgodnie z sugestią @Andreas):

let diff, result = timer (g x1 x2 x3 ... xN-1) xN 
+0

Jeśli wiesz, że funkcja nie robi nic ciekawego po zastosowaniu częściowo (jak większość funkcji), to nie potrzebujesz nawet abstrakcji. Wystarczy wywołać 'timer (g x1 x2 ... xN-1) xN'. –

+0

@AndreasRossberg: Dzięki, włączyłem twój punkt do mojej odpowiedzi. – pad

7

Uwaga dotycząca rozwiązania podkładki, które było zbyt szczegółowe, aby pasowało do komentarza.

W praktyce okazało się, że lepszym rozwiązaniem jest wymuszenie f : unit -> 'a przez podanie () zamiast opóźnionego argumentu.

let timer f = 
    let t0 = Sys.time() in 
    let result = f() in 
    let t1 = Sys.time() in 
    t1 -. t0, result 

Powodem jest to, że mają tendencję do używania poniższego wzoru dość często:

let fun_to_test = match some_configuration with ... in 
timer fun_to_test 

projekt pad, który jest bardziej atrakcyjne na początku, bo bardziej ogólnego, zachęca do zamiast pisać:

Problem z tym wyborem polega na tym, że początkowo wydaje się być w porządku, a po dodaniu kilku opcji napotkasz przypadek, w którym argumenty różnych testowanych funkcji nie należą do s ame typu. I masz błąd typu. Przykład złym kodu:

let fun_to_test, arg = 
    if Random.bool() 
    then (foo, 3) 
    else (bar, 3.2) 
in timer fun_to_test arg 

Poprzez wymuszenie zamknięcia z parametrami wstępnie przeszły, otrzymuję „typ egzystencjalny” za darmo tutaj: typ ostatniej funkcji argumentu nie pojawia się w rodzaju timer podanie. Stwierdziłem, że jest to lepsze w praktyce.

Oczywiście można również opóźnić pełne połączenie i użyć () jako argumentu w projekcie pada. Ale wolę wybór, który zmusza mnie do zrobienia tego, ponieważ w przeciwnym razie jestem zbyt kuszony, aby tego nie robić, a ja zapłacę później.

+0

moje doświadczenie sekunduje to – ygrek