2016-03-31 3 views
5

Я пытаюсь сделать в Julia то, что делает этот код Python. (Найти все пары из двух списков, суммарное значение выше 7.)List comprehensions and tuples 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) 

Там нет «если» для списочных в Джулию. И если я использую filter(), я не уверен, могу ли я передать два аргумента. Поэтому мое лучшее предложение:

#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) 

Я не считаю это очень привлекательным. Итак, мой вопрос: есть ли лучший способ в Джулии?

ответ

6

Используя очень популярный пакет Iterators.jl, in Julia:

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

является итератором, производящим пары. Таким образом, чтобы получить ту же распечатку, что и OP:

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

Итератор подход потенциально гораздо больше памяти. Конечно, можно было просто l3 = collect(l3iter) получить парный вектор.

@ user2317519, просто любопытно, существует ли эквивалентная форма итератора для python?

+1

Мне нравятся итераторы. Вы можете сделать подобное в python, если импортируете itertools. Однако, боюсь, я ошибся, пытаясь свести свой вопрос к простому примеру. Моя основная проблема связана с кортежем как аргументом. Можем ли мы сделать вид, что функция sum_is_large() действительно довольно сложна и что ей необходимо повторно использовать переменные a и b. Есть ли способ передать эти две переменные, не делая что-то вроде a, b = pair? –

+0

'a' и' b' адресуются как 'x [1]' и 'x [2]' в 'bigfunc'. Вы также можете сделать 'x-> bigfunc (x ...)', а затем определить 'bigfunc (a, b)'. Этот кодовый объезд не должен стоить слишком дорого, если вообще. –

+1

@DanGetz в python есть [itertools] (https://docs.python.org/2/library/itertools.html), который в основном содержит похожие итераторы, которые julia's 'Iterators.jl'. –

1

Возможно, что-то вроде этого:

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) 

хотя я согласен, что это не выглядит, как это должно быть лучшим способом ...

2

Другой вариант похож на один из @DanGetz также с использованием Iterators.jl:

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

Тогда, если условие является также сложным, он может быть определен как функция:

condition(x) = x > 7 

И последняя , фильтровать результаты:

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

result is iterable который вычисляется только при необходимости (недорогой) и может быть собран collect(result), если требуется.

Однолинейное, если условие фильтра достаточно просто будет:

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

Примечание: imap работает изначально для любого числа параметров.

0

Я удивлен, что никто не упоминает о тройном операторе реализовать условные:

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) 

или даже просто нормальный if блока внутри составного оператора, т.е.

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

, который дает тот же результат ,

Я чувствую, что это результат делает намного больше смысла, чем filter(), потому что в Джулии a in A, b in B конструкция интерпретируется размерно, и, следовательно, выход в действительности является «массив понимания» с соответствующей размерностью, которая явно во многих случаи были бы выгодными и, предположительно, желательным поведением (будем ли мы включать условные или нет).

Принимая во внимание, что фильтр всегда будет возвращать вектор. Очевидно, если вы действительно хочу, чтобы векторный результат вы всегда можете получить collect результат; или для условного понимания списка, как здесь, вы можете просто удалить nothing элементов из массива, выполнив l3 = l3[l3 .!= nothing].

Предположительно, это все еще яснее и не менее эффективно, чем подход filter().

0

Вы можете использовать макрос @vcomp (вектор понимания) в VectorizedRoutines.jl сделать Python-как постижения:

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

гвардии (if) теперь доступен в Джулии v0.5 (в настоящее время в release- кандидат этап):

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) 

Обратите внимание, что генераторы теперь доступны также:

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]))) 
Смежные вопросы