2015-12-24 11 views
9

In Haskell, esiste una funzione di linguaggio chiamata "as" -operator (talvolta noto come alias). L'idea è la seguente: proposta si ha una funzione che ad esempio prende in input una lista e vuole tornare tutte le code, è possibile implementare questa come:Prolog ha un alias "operatore" come Haskell?

tails [email protected](_:xs) = a : tails xs 
tails [] = [[]] 

la @ assicura di avere un riferimento sia alla argomento intero nonché un riferimento ad alcune parti della struttura del parametro. Questo è intelligente dal punto di vista delle prestazioni (è più un trucco per le prestazioni, perché ricostruendo un array (x:xs)) nel corpo della prima linea, se non ottimizzato dal compilatore, si verificherà l'allocazione di un nuovo oggetto, la modifica dei campi, ecc. here per ulteriori informazioni.

mi chiedevo se Prolog ha qualcosa di equivalente: per esempio se si desidera implementare le code in Prolog, si può fare in questo modo:

tails([H|T],[[H|T]|TA]) :- 
    tails(T,TA). 
tails([],[[]]). 

ma potrebbe essere più efficace se ci fosse un " come "-operatore:

tails([email protected][_|T],[L|TA]) :- %This does not compile 
    tails(T,TA). 
tails([],[[]]). 

Esiste un tale costrutto o un'estensione di lingua?

+2

In prolog è possibile evitare lo pseudonimo. Basta usare le code (L, [L | TA]): - L = [_ | T], ... '. – Bakuriu

+0

Sono d'accordo, ma sarebbe bello se ciò fosse possibile nella testa. (So ​​che sono fastidioso: S) –

risposta

8

TL; DR: Buona idea ! L'accelerazione sembra essere limitata a ~ 20% (per la maggior parte delle dimensioni delle liste).

In questa risposta, mettiamo a confronto tre diversi predicati che differiscono per quanto riguarda il riutilizzo @ -come dati:

 
list_tails([], [[]]).    % (1) like `tails/2` given by the OP ... 
list_tails([E|Es], [[E|Es]|Ess]) :- %  ....... but with a better name :-) 
    list_tails(Es, Ess). 

list_sfxs1(Es, [Es|Ess]) :-   % (2) "re-use, mutual recursion" 
    aux_list_sfxs1(Es, Ess).   %  "sfxs" is short for "suffixes" 

aux_list_sfxs1([], []). 
aux_list_sfxs1([_|Es], Ess) :- 
    list_sfxs1(Es, Ess). 

list_sfxs2([], [[]]).    % (3) "re-use, direct recursion" 
list_sfxs2(Es0, [Es0|Ess]) :- 
    Es0 = [_|Es], 
    list_sfxs2(Es, Ess). 

Per misurare runtime, usiamo il seguente codice:

 
:-( dif (D, sicstus), current_prolog_flag (dialect ,D) 
    ; use_module (library(between)) 
). 

run_benchs(P_2s, P_2, L, N, T_ms) :- 
    between (1, 6, I), 
    L  is 10 ^ I, 
    N is 10^(8-I), 
    length (Xs, L), 
    member (P_2, P_2s), 
    garbage_collect , 
    call_walltime(run_bench_core(P_2,Xs,N), T_ms). 

run_bench_core(P_2, Xs, N) :- 
    between(1, N, _), 
    call (P_2, Xs, _), 
    false . 
run_bench_core(_, _, _). 

Per misurare , utilizziamo call_ walltime /2 - una variazione di call_time/2:

 
call_walltime(G, T_ms) :- 
    statistics (walltime , [T0|_]), 
    G, 
    statistics(walltime, [T1|_]), 
    T_ms is T1 - T0. 

Mettiamola variazioni codice sopra alla prova ...

  • ... attraverso lista diverse lunghezze L ...
  • ... e funzionante ogni prova diverse volte N (per migliore precisione).

In primo luogo, usiamo versione 7.3.14 (64 bit):

 
?- run_benchs([list_sfxs1,list_sfxs2,list_tails], P_2, L, N, T_ms). 
    P_2 = list_sfxs1, L*N = 10*10000000, T_ms = 7925 
; P_2 = list_sfxs2, L*N = 10*10000000, T_ms = 7524 
; P_2 = list_tails, L*N = 10*10000000, T_ms = 6936 
; 
    P_2 = list_sfxs1, L*N = 100*1000000, T_ms = 6502 
; P_2 = list_sfxs2, L*N = 100*1000000, T_ms = 5861 
; P_2 = list_tails, L*N = 100*1000000, T_ms = 5618 
; 
    P_2 = list_sfxs1, L*N = 1000*100000, T_ms = 6434 
; P_2 = list_sfxs2, L*N = 1000*100000, T_ms = 5817 
; P_2 = list_tails, L*N = 1000*100000, T_ms = 9916 
; 
    P_2 = list_sfxs1, L*N = 10000*10000, T_ms = 6328 
; P_2 = list_sfxs2, L*N = 10000*10000, T_ms = 5688 
; P_2 = list_tails, L*N = 10000*10000, T_ms = 9442 
; 
    P_2 = list_sfxs1, L*N = 100000*1000, T_ms = 10255 
; P_2 = list_sfxs2, L*N = 100000*1000, T_ms = 10296 
; P_2 = list_tails, L*N = 100000*1000, T_ms = 14592 
; 
    P_2 = list_sfxs1, L*N = 1000000*100, T_ms = 6955 
; P_2 = list_sfxs2, L*N = 1000000*100, T_ms = 6534 
; P_2 = list_tails, L*N = 1000000*100, T_ms = 9738. 

Poi, si ripete la query precedente utilizzando versione 4.3.2 (64-bit):

 
?- run_benchs([list_sfxs1,list_sfxs2,list_tails], P_2, L, N, T_ms). 
    P_2 = list_sfxs1, L*N = 10*10000000, T_ms = 1580 
; P_2 = list_sfxs2, L*N = 10*10000000, T_ms = 1610 
; P_2 = list_tails, L*N = 10*10000000, T_ms = 1580 
; 
    P_2 = list_sfxs1, L*N = 100*1000000, T_ms = 710 
; P_2 = list_sfxs2, L*N = 100*1000000, T_ms = 750 
; P_2 = list_tails, L*N = 100*1000000, T_ms = 840 
; 
    P_2 = list_sfxs1, L*N = 1000*100000, T_ms = 650 
; P_2 = list_sfxs2, L*N = 1000*100000, T_ms = 660 
; P_2 = list_tails, L*N = 1000*100000, T_ms = 740 
; 
    P_2 = list_sfxs1, L*N = 10000*10000, T_ms = 620 
; P_2 = list_sfxs2, L*N = 10000*10000, T_ms = 650 
; P_2 = list_tails, L*N = 10000*10000, T_ms = 740 
; 
    P_2 = list_sfxs1, L*N = 100000*1000, T_ms = 670 
; P_2 = list_sfxs2, L*N = 100000*1000, T_ms = 650 
; P_2 = list_tails, L*N = 100000*1000, T_ms = 750 
; 
    P_2 = list_sfxs1, L*N = 1000000*100, T_ms = 12610 
; P_2 = list_sfxs2, L*N = 1000000*100, T_ms = 12560 
; P_2 = list_tails, L*N = 1000000*100, T_ms = 33460. 

Sommario:

  • Il alias-thingy lattina e fa migliorare le prestazioni in modo significativo.
  • Nei test di cui sopra, il SICStus Prolog 10X aumento di velocità, rispetto a SWI-Prolog!

nota 1: Perché i bravata di mettere (@)/2 nella testa regola? Per finire con codice non idiomatic Prolog?
Nota 2: Siamo interessati al runtime totale. Perché? Perché i costi di raccolta rifiuti mostrano con dimensioni di dati più grandi!
Nota 3: La sequenza di risposta è stata post-elaborata per motivi di leggibilità.
Nota 4: Disponibile dal release 4.3.0. Le attuali architetture di destinazione includono IA-32 e AMD64.

+0

Mi chiedo se mettere in testa qualcosa del genere non porti a ulteriori ottimizzazioni. Per 'the tails', non è molto utile, ma ora si rimandano i controlli che * potrebbe * essere stato fatto prima di chiamare quel predicato. Tuttavia, risposta impressionante +1. –

+0

Inoltre mi chiedo se il compilatore non possa essere migliorato per individuare che '[E | Es]' è usato due volte e quindi costruire un alias "implicito" stesso. –

+3

@WillemVanOnsem. Sì, in linea di principio, i compilatori Prolog * potrebbero * farlo. OTOH in un linguaggio funzionale puro con valutazione lenta (come Haskell) questo è ancora più diretto. La compilazione del Prolog è complicata, se vuoi assicurarti che la compilazione e il codice interpretato analogico non si comportino mai ** in alcun modo. Un sacco di sottigliezze/dettagli devono essere risolti correttamente. SICStus JIT è relativamente nuovo ... e molto impressionante! – repeat