Есть ли функция, которая позволяет мне контролировать количество аргументов? - PullRequest
3 голосов
/ 27 июня 2019

У меня есть следующий код:

circ(x) = x./sqrt(sum(x .* x))

x -> cat(circ(x), circ(x); dims = 1)

но я хочу иметь возможность создать функцию, в которую я ввожу число, и оно объединяет это число в кружке (х).

так например:

function Ncircs(n)
  #some way to make cat() have as its parameter circ n number of times
end

и я могу позвонить Ncircs(2) и получить x -> cat(circ(x), circ(x); dims = 1) или Ncircs(3) и получите x -> cat(circ(x), circ(x), circ(x); dims = 1) или Ncircs(4) и получите x -> cat(circ(x), circ(x), circ(x), circ(x); dims = 1)

и т.д.

Есть ли способ сделать это? Нужно ли использовать макрос?

1 Ответ

3 голосов
/ 27 июня 2019

Вы можете написать:

Ncircs(n) = x -> cat(Iterators.repeated(circ(x), n)...; dims = 1)

, и если вы знаете, что вы будете делать dims=1 всегда, тогда выполните репликацию cat с vcat и reduce

Ncircs(n) = x -> reduce(vcat, Iterators.repeated(circ(x), n))

будет более эффективным для больших n.

В качестве примечания: использование другой опции (vcat) приведет к стабильному типу результата, тогда как первая опция не стабильна по типу.

EDIT

Почему сокращение пустой коллекции не допускается?

В общем, причина в том, что тогда вы не можете сказать, что должно быть результатом сокращения. Если вы хотите разрешить пустую коллекцию, вы должны добавить init аргумент ключевого слова. Вот пример:

julia> reduce(vcat, [])
ERROR: ArgumentError: reducing over an empty collection is not allowed

julia> reduce(vcat, [], init = [1])
1-element Array{Int64,1}:
 1

julia> reduce(vcat, [[2,3], [4,5]], init = [1])
5-element Array{Int64,1}:
 1
 2
 3
 4
 5

Что это означает, что результат является стабильным типом

Это означает, что Джулия может определить тип возвращаемого значения функции во время компиляции (перед выполнением кода). Тип стабильного кода, как правило, работает быстрее (хотя это широкая тема - я рекомендую вам прочитать руководство Julia, чтобы понять его подробно). Вы можете проверить, является ли функция стабильной по типу, используя @code_warntype и Test.@inferred.

Здесь позвольте мне дать вам объяснение в вашем конкретном случае (я сократил некоторые результаты, чтобы сократить ответ).

julia> x = [1,2,3]
3-element Array{Int64,1}:
 1
 2
 3

julia> y = [4,5,6]
3-element Array{Int64,1}:
 4
 5
 6

julia> @code_warntype vcat(x,y)
Body::Array{Int64,1}
...

julia> @code_warntype cat(x,y, dims=1)
Body::Any
...

julia> using Test

julia> @inferred vcat(x,y)
6-element Array{Int64,1}:
 1
 2
 3
 4
 5
 6

julia> @inferred cat(x,y, dims=1)
ERROR: return type Array{Int64,1} does not match inferred return type Any

Any выше означает, что компилятор не знает, какой будет тип ответа. Причина в том, что этот тип зависит от параметра dims. Если это 1, то это будет вектор, если это 2, то это будет матрица.

Откуда я знаю, что это будет более эффективно для больших n

Вы можете запустить @which макрос:

julia> @which reduce(vcat, [[1,2,3], [4,5,6]])
reduce(::typeof(vcat), A::AbstractArray{#s72,1} where #s72<:(Union{AbstractArray{T,2}, AbstractArray{T,1}} where T)) in Base at abstractarray.jl:1321

И вы видите, что есть специальный reduce метод для vcat.

Теперь, если вы запустите:

@edit reduce(vcat, [[1,2,3], [4,5,6]])

Откроется редактор, и вы увидите, что он вызывает внутреннюю функцию _typed_vcat, оптимизированную для vcat, что позволяет использовать множество массивов. Эта оптимизация была введена, потому что использование такого сплаттинга, как этот vcat([[1,2,3], [4,5,6]]...), эквивалентно в результате, но вы должны сделать сплаттинг (...), который сам по себе имеет определенную стоимость, которую можно избежать, используя версию reduce.

Чтобы убедиться, что то, что я говорю, верно, вы можете выполнить следующий тест:

julia> using BenchmarkTools

julia> y = [[i] for i in 1:10000];

julia> @benchmark vcat($y...)
BenchmarkTools.Trial:
  memory estimate:  156.45 KiB
  allocs estimate:  3
  --------------
  minimum time:     67.200 μs (0.00% GC)
  median time:      77.800 μs (0.00% GC)
  mean time:        102.804 μs (8.50% GC)
  maximum time:     35.179 ms (99.47% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark reduce(vcat, $y)
BenchmarkTools.Trial:
  memory estimate:  78.20 KiB
  allocs estimate:  2
  --------------
  minimum time:     67.700 μs (0.00% GC)
  median time:      69.700 μs (0.00% GC)
  mean time:        82.442 μs (6.39% GC)
  maximum time:     32.719 ms (99.58% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark cat($y..., dims=1)
ERROR: StackOverflowError:

И вы видите, что reduce версия немного быстрее, чем всплывающая версия vcat, в то время как cat просто терпит неудачу для очень большого n (для меньшего n это будет работать, но просто будет медленнее).

...