2012-11-05 9 views
5

Sto cercando di codificare un Erlang Mastermind risolutore come esercizio (io sono un novizio completo, ma mi sa che è un esercizio interessante per un linguaggio funzionale)Potenza cartesiana di una lista in Erlang

lo voglio per essere il più generale possibile, quindi sento di aver bisogno di una funzione di alimentazione cartesiana. Qualcosa di simile:

cart_pow([a,b],2) -> [[a,a],[a,b],[b,a],[b,b]] 
cart_pow([a,b],3) -> [[a,a,a],[a,a,b],[a,b,a],[a,b,b],[b,a,a],[b,a,b],[b,b,a],[b,b,b]] 

Non riesco a pensare ad una soluzione puramente funzionale (ricorsiva, mappa, piega ...). Eventuali indizi? Bonus se è pigro.

risposta

1

È possibile trovare questa domanda di sovraccarico dello stack utile, che si occupa della generazione della potenza cartesiana di un elenco in lingue funzionali. La domanda è mirato per F #, ma c'è un esempio Haskell nei commenti così: F#: how to find Cartesian power

2

Da implementazione Haskell:

cart_pow(Xs, N) -> 
    sequence(lists:duplicate(N, Xs)). 

sequence([]) -> 
    [[]]; 
sequence([Xs|Xss]) -> 
    [[X|Xs1] || X <- Xs, Xs1 <- sequence(Xss)]. 

Non sei sicuro di come si può fare liste di Erlang pigro però.

Aggiornamento: Questa versione può essere migliorato in termini di prestazioni, semplicemente rendendo ricorsiva in coda (anche se credo che ci sia alcuna differenza asintotica tra tutti e tre)

cart_pow(Xs, N) -> 
    sequence(lists:duplicate(N, Xs)). 

sequence(Xss) -> 
    sequence(Xss, [[]]). 

sequence([], Acc) -> 
    Acc; 
sequence([Xs|Xss], Acc) -> 
    sequence(Xss, [[X|Xs1] || X <- Xs, Xs1 <- Acc]). 

in confronto con @ La versione di Stemm:

1> timer:tc(fun() -> length(tmp1:cart([0,1], 20)) end). 
{383939,1048576} 
2> timer:tc(fun() -> length(tmp1:cart_pow([0,1], 20)) end). 
{163932,1048576} 

PS: O ancora meglio:

sequence(Xss) -> 
    lists:foldl(fun(Xs, A) -> [[X|Xs1] || X <- Xs, Xs1 <- A] end, [[]], Xss). 
3

La soluzione fornita da @ Ed'ka è laconica e piacevole, ma nonostante ciò, la sua complessità è O(N).

Si consiglia di prendere in considerazione Exponentiation by squaring method, che fornisce la complessità O(log(N)) nei calcoli di potenza. Utilizzando questa tecnica, il potere cartesiano può essere implementato in questo modo:

%% Entry point 
cart(List, N) -> 
     Tmp = [[X] || X <- List], 
     cart(Tmp, Tmp, N). 

cart(_InitialList, CurrList, 1) -> 
     CurrList; 
cart(_InitialList, CurrList, N) when N rem 2 == 0 -> 
     Tmp = mul(CurrList, CurrList), 
     cart(Tmp, Tmp, N div 2); 
cart(InitialList, CurrList, N) -> 
     Tmp = cart(InitialList, CurrList, N - 1), 
     mul(InitialList, Tmp). 

mul(L1, L2) -> 
     [X++Y || X <- L1, Y <- L2]. 

P.S. Esempio di utilizzo da shell (ho funzione cart confezionato in mudule my_module):

1> c(my_module). 
{ok,my_module} 
2> 
2> my_module:cart([0,1], 2). 
[[0,0],[0,1],[1,0],[1,1]] 
3> 
3> my_module:cart([0,1], 3). 
[[0,0,0], 
[0,0,1], 
[0,1,0], 
[0,1,1], 
[1,0,0], 
[1,0,1], 
[1,1,0], 
[1,1,1]] 
+0

mul era il pezzo che mancava. Non riuscivo a ottenere che restituisse la struttura corretta, principalmente perché II non capiva che l'input corretto non era ([a, b], [a, b]) ma ([[a], [b]] , [[a], [b]]) – faibistes

+0

In funzione 'mul' ha calcolato il prodotto cartesiano di due liste. Si può notare che la sintassi di concatenazione degli elenchi utilizzata lì (operatore '++'). Quindi, significa che quella funzione funziona solo con elenchi di elenchi. Ecco perché devi chiamare 'mul ([[a], [b]], [[a], [b]])' invece di 'mul ([a, b], [a, b])'. Se guardi il punto di ingresso (funzione 'cart/2') - potresti notare che io ** trasformo la lista di input in lista di liste ** (avvolgendo ciascun elemento nel suo elenco:' [[X] || X <- List] ', quindi list '[a, b]' si trasforma in '[[a], [b]]'). – stemm

+0

Sarebbe O (log (N)) se l'operatore '++' era O (1) ma sfortunatamente è O (N) (dove N è la lunghezza dell'argomento a sinistra) così efficacemente stai solo sostituendo più '[ |] 'con un singolo' ++ '. La tua soluzione funziona più velocemente (una differenza costante) rispetto alla prima versione della mia, ma sospetto che sia dovuto alla coda chiamata nel secondo caso.Per esempio se faccio la mia soluzione in modo ricorsivo (vedi il mio aggiornamento) diventa più veloce della tua (di nuovo, una differenza costante). –