2016-03-31 14 views
5

Sto cercando di fare in Julia cosa fa questo codice Python. (Trova tutte le coppie delle due liste il cui valore complessivo è superiore a 7.)Comprensioni e tuple delle liste in Julia

#Python  
def sum_is_large(a, b): 
    return a + b > 7 

l1 = [1,2,3] 
l2 = [4,5,6] 
l3 = [(a,b) for a in l1 for b in l2 if sum_is_large(a, b)] 
print(l3) 

non c'è 'se' per la lista comprensioni a Giulia. E se utilizzo filter(), non sono sicuro di poter passare due argomenti. Quindi il mio miglior suggerimento è questo:

#Julia 
function sum_is_large(pair) 
    a, b = pair 
    return a + b > 7 
end 

l1 = [1,2,3] 
l2 = [4,5,6] 

l3 = filter(sum_is_large, [(i,j) for i in l1, j in l2]) 
print(l3) 

Non trovo questo molto allettante. Quindi la mia domanda è, c'è un modo migliore in Julia?

risposta

6

Usando il pacchetto molto popolare Iterators.jl, in Julia:

using Iterators  # install using Pkg.add("Iterators") 
filter(x->sum(x)>7,product(l1,l2)) 

è un iteratore che produce le coppie. Quindi, per ottenere la stessa stampa dell'OP:

l3iter = filter(x->sum(x)>7,product(l1,l2)) 
for p in l3iter println(p); end 

L'approccio iteratore è potenzialmente molto più efficiente in termini di memoria. Naturalmente, uno potrebbe solo l3 = collect(l3iter) per ottenere il vettore coppia.

@ user2317519, solo curioso, esiste un modulo iteratore equivalente per python?

+1

Mi piacciono gli iteratori. Si può fare una cosa simile in python se si importano itertools. Tuttavia, temo di aver commesso un errore quando ho cercato di ridurre la mia domanda a un semplice esempio. Il mio problema principale è con la tupla come argomento. Possiamo fingere che la funzione sum_is_large() sia davvero piuttosto complicata e che debba usare ripetutamente le variabili a e b. C'è un modo per passare queste due variabili senza fare qualcosa come a, b = coppia? –

+0

'a' e' b' sono indirizzabili come 'x [1]' e 'x [2]' in 'bigfunc'. Puoi anche fare 'x-> bigfunc (x ...)' e quindi definire 'bigfunc (a, b)'. Questa deviazione del codice non dovrebbe costare troppo in termini di efficienza, semmai. –

+1

@DanGetz in python c'è [itertools] (https://docs.python.org/2/library/itertools.html) che contiene fondamentalmente iteratori simili a 'Iterators.jl' di julia. –

1

Forse qualcosa di simile:

julia> filter(pair -> pair[1] + pair[2] > 7, [(i, j) for i in l1, j in l2]) 
3-element Array{Tuple{Any,Any},1}: 
(3,5) 
(2,6) 
(3,6) 

anche se ero d'accordo che non sembra che dovrebbe essere il modo migliore ...

2

Un'altra opzione simile a quella di @DanGetz usare anche Iterators.jl:

function expensive_fun(a, b) 
    return (a + b) 
end 

Quindi, se la condizione è complicata, può essere definito come una funzione:

condition(x) = x > 7 

E ultimo , filtrare i risultati:

>>> using Iterators 
>>> result = filter(condition, imap(expensive_fun, l1, l2)) 

result è un iterabile viene calcolato solo quando necessario (economico) e può essere raccolto collect(result) se necessario.

La sola riga, se la condizione di filtro è abbastanza semplice sarebbe:

>>> result = filter(x->(x > 7), imap(expensive_fun, l1, l2)) 

Nota: imap funziona in modo nativo per il numero arbitrario dei parametri.

0

Mi sorprende che nessuno menziona l'operatore ternario per implementare la condizionale:

julia> l3 = [sum_is_large((i,j)) ? (i,j) : nothing for i in l1, j in l2] 
3x3 Array{Tuple,2}: 
nothing nothing nothing 
nothing nothing (2,6) 
nothing (3,5) (3,6) 

o anche solo un normale if blocco all'interno di un'istruzione composta, cioè

[ (if sum_is_large((x,y)); (x,y); end) for x in l1, y in l2 ] 

che dà lo stesso risultato .

ritengo questo risultato rende molto più senso di filter(), perché in julia costrutto a in A, b in B viene interpretato dimensionalmente, e quindi l'uscita è in realtà una "comprensione matrice" con adeguate dimensionalità, che chiaramente in molti i casi sarebbero vantaggiosi e presumibilmente il comportamento desiderato (includendo o meno un condizionale).

Mentre il filtro restituirà sempre un vettore. Ovviamente, se tu davvero vuoi un risultato vettoriale puoi sempre ottenere il risultato collect; o per una comprensione di lista condizionale come quella qui, puoi semplicemente rimuovere gli elementi nothing dall'array facendo l3 = l3[l3 .!= nothing].

Presumibilmente questo è ancora più chiaro e non meno efficiente rispetto all'approccio filter().

0

È possibile utilizzare la macro @vcomp (vettore di comprensione) in VectorizedRoutines.jl fare comprensioni Python-like:

using VectorizedRoutines 
[email protected] Int[i^2 for i in 1:10] when i % 2 == 0 # Int[4, 16, 36, 64, 100] 
3

Guardie (if) sono ora disponibili in Julia v0.5 (attualmente in release- fase candidato):

julia> v1 = [1, 2, 3]; 

julia> v2 = [4, 5, 6]; 

julia> v3 = [(a, b) for a in v1, b in v2 if a+b > 7] 
3-element Array{Tuple{Int64,Int64},1}: 
(3,5) 
(2,6) 
(3,6) 

Nota che i generatori sono ora disponibili anche:

julia> g = ((a, b) for a in v1, b in v2 if a+b > 7) 
Base.Generator{Filter{##18#20,Base.Prod2{Array{Int64,1},Array{Int64,1}}},##17#19}(#17,Filter{##18#20,Base.Prod2{Array{Int64,1},Array{Int64,1}}}(#18,Base.Prod2{Array{Int64,1},Array{Int64,1}}([1,2,3],[4,5,6]))) 
Problemi correlati