Julia: Устарели `ones`?

Созданный на 2 нояб. 2017  ·  46Комментарии  ·  Источник: JuliaLang/julia

Теперь, когда мы различаем one и oneunit , название ones(T, sz) кажется неправильным. Устарели в пользу fill(oneunit(T), sz) ? Если да, то должны ли мы тоже отказаться от zeros ?

decision linear algebra stdlib

Самый полезный комментарий

Для меня fill(oneunit(T), sz) выглядит нетривиальной потерей читабельности по сравнению с ones(T, sz) .

Все 46 Комментарий

xref https://github.com/JuliaLang/julia/issues/11557#issuecomment -339776065 и ниже, а также PR №24389 от @ Sacha0

У меня есть работа в этом направлении, которую я надеюсь опубликовать в ближайшие день или два :). Лучший!

Для меня fill(oneunit(T), sz) выглядит нетривиальной потерей читабельности по сравнению с ones(T, sz) .

Обратите внимание, что вам редко нужно писать что-то столь подробное, как fill(oneunit(T), sz) , поскольку обычно вместо oneunit(T) достаточно литерала или чего-то подобного компактного. Более короткие заклинания также могут стать возможными с будущими изменениями конструкторов массивов. Кроме того, как только вы перешли на fill , он вам понравится, уверяю вас :). Лучший!

Мы могли просто выбрать, использует ли ones one или oneunit . ones и zeros следует рассматривать как вспомогательные функции, что нормально, если они имеют четкое значение с точки зрения более общих функций.

План для каждой сортировки: пересмотрите на следующей неделе, особенно учитывая перемещение ones и zeros на уровень совместимости MATLAB в пользу fill в base. Лучший!

ones и oneunits будут различать эти два варианта.

Не уверен, как я отношусь к перемещению этого и zeros в совместимость с MATLAB. То, что это было изобретено Mathworks, не означает, что это не очень удобный API, которым мы можем гордиться . Вкратце - каковы были причины этой мысли? (Извините, я должен сказать, что сортировка кажется намного более эффективной, но значительно менее прозрачной, когда не приводится аргументация).

Я тоже не в восторге от этого изменения, но я поднял его, потому что это логическая непоследовательность. Я думаю, будет справедливо сказать, что ones(T, sz) подразумевает fill(one(T), sz) но на самом деле то, что он делает под капотом, - это fill(oneunit(T), sz) .

Один из вариантов - переименовать его в oneunits . Альтернативой может быть ужасный обмен: one -> algebraic_one и oneunit -> one . Это ломается, и это невозможно сделать через устаревание, поэтому я говорю «ужасно».

Да, я бы сказал, что добавление oneunits и изменение ones на fill(one(T), ...) было бы очевидным решением этой проблемы, не так ли?

Я бы с этим согласился. Из любопытства, как используются fill(one(T), sz) ? Нужен ли нам вообще ones ?

Ха-ха :) Я собирался спросить - для чего вы используете fill(oneunits(T), sz) ? (Например, для чего можно использовать массив, заполненный 1 метром, 1 килограммом или 1 секундой?)

Я думаю, что fill(one(T), sz) используется во многом по той же причине, что и zeros(T, sz) , то есть для инициализации массива для операции настраиваемого типа редукции. Массив z = zeros(T, sz) готов к добавлению его элементов с помощью z[i] += x тогда как o = fill(one(T), sz) готов к умножению его элементов o[i] *= x . Например, я думаю о ситуациях, когда элементы массива могут представлять (относительную) вероятность. В этих двух случаях я ищу идентичность для оператора + или * соответственно (а не для аддитивных генераторов).

См. Также # 23544

Ха-ха :) Я собирался спросить - для чего вы используете fill (oneunits (T), sz)? (Например, для чего можно использовать массив, заполненный 1 метром, 1 килограммом или 1 секундой?)

Зависимые переменные для ОДУ. Было бы странно, если бы он просто случайным образом нарезал единицы, когда вы запросили массив типа T ?

Основная причина предпочесть fill заключается в том, что ограниченный набор мощных, ортогональных, компонуемых инструментов работает лучше, чем большой набор специальных, ограниченных и перекрывающихся инструментов, таких как ones / zeros / trues / falses . Приведенное выше обсуждение подчеркивает этот момент: в то время как fill однозначно и (в большинстве случаев реального использования) содержит все варианты использования выше, ones / zeros / trues / falses подход требует новой функции для каждого варианта использования.

Пара актуальных примеров из рерайтов в базе:

complex.(ones(size(Ac, 1)), ones(size(Ac, 1)))

становится

fill(1.0 + 1.0im, size(Ac, 1))

а также

2ones(Int, 2, 2, 2)

становится

fill(2, (2, 2, 2)) # or fill(2, 2, 2, 2) if you prefer

Обратите внимание, что эти заклинания fill проще, удобочитаемы, компактнее и эффективнее, чем их аналоги, отличные от fill , а base/ и test/ - изобилует подобными примерами. Как и в случае с любыми другими изменениями, переход на fill требует некоторой первоначальной психологической адаптации. Но после настройки вы обнаружите, что у вас под рукой больше мощности и элегантности :). Лучший!

@ Sacha0 : trues / falses нельзя напрямую заменить на fill , но необходимо использовать fill! с инициализатором BitArray . Они также не входят в двусмысленность между one и oneunit . Поэтому я не думаю, что они вообще подходят для этой дискуссии.

Что касается ones , я вообще против его прекращения, я не вижу в этом пользы. Аргумент, что какое-то выражение может быть написано более эффективно с помощью fill на мой взгляд, не очень убедителен, поскольку во всех этих примерах ones качестве промежуточных шагов для выполнения чего-то другого; но как насчет того, когда вам действительно нужен массив из них? Тогда необходимость использовать fill дольше, менее очевидна и просто более раздражает. Мне больше всего нравится предложение @JeffBezanson . Если части base или test можно переписать более эффективно, используя fill вместо ones то сейчас ничто не препятствует этому.

@carlobaldassi Хотя это правда, быстрый поиск по GitHub показывает, что почти во всех случаях использования ones действительно следует использовать fill чтобы избежать промежуточных распределений ...

Для меня имеет смысл использовать fill для этих случаев. Обратите внимание, что нам понадобится новый метод fill(x, A) = fill!(x, copymutable(A)) для замены соответствующих методов ones и т. Д.

@TotalVerb Беглый взгляд на первые 10 страниц этого поиска, я бы не сказал, что «почти все» - это справедливая оценка. В лучшем случае «значительная доля». Есть много-много законных вариантов использования ones , возможно, даже большинство (и даже если бы они были всего лишь 20%, я думаю, что мой аргумент все еще в силе).

(У меня также есть оговорки по поводу аргумента о читабельности, я думаю, сомнительно утверждать, что, например, fill(2, 2, 2, 2) более читабельно, чем 2 * ones(Int, 2, 2, 2) , или что, скажем, fill(k * 1.0, 3) было бы более читабельнее, чем k * ones(3) ; я совсем не уверен, что это просто дело привычки. Хотя это второстепенный момент.)

истины / фальсификации нельзя напрямую заменить заливкой, но необходимо использовать заливку! с инициализатором BitArray. Они также не входят в двусмысленность между one и oneunit. Поэтому я не думаю, что они вообще подходят для этой дискуссии.

Действительно, trues и falses нельзя напрямую заменить на fill :). Скорее, trues и falses являются дополнительными экземплярами общей проблемы, описанной выше / связанной с # 11557 (и которую, мы надеемся, решит недавнее направление конструкторов массивов). Другие примеры включают существование, например, bones , bzeros , brand , brandn и beye в BandedMatrices.jl и их эквивалентов с Префикс d в DistributedArrays.jl.

Что касается некоторых, я вообще против того, чтобы отказываться от них, я не вижу в этом пользы. Аргумент, что некоторые выражения могут быть более эффективно записаны с помощью fill, на мой взгляд, не очень убедителен, поскольку во всех этих примерах они используются в качестве промежуточных шагов для выполнения чего-то еще.

Только что переписав несколько сотен использований ones в базе, я могу подтвердить утверждение @TotalVerb

быстрый поиск на GitHub показывает, что почти все их использования действительно должны использовать fill, чтобы избежать промежуточных распределений ...

(Изменить: хотя я бы сказал примерно половину, а не почти все, и соответствующие перезаписи могут быть чем-то другим, кроме fill .) Более того, этот опыт перезаписи научил меня ...

но как насчет того, когда вам действительно нужен массив из них? Тогда необходимость использовать заливку становится более длительной, менее очевидной и просто более раздражающей.

... с другой стороны, fill часто короче и проще в этом случае: запрошенные часто не Float64 (вместо этого, например, ones(Int, n...) и ones(Complex{Float64}, n...) ), и в этом случае fill короче и проще за счет допуска литерала (например, fill(1, n...) и fill(1.0 + 0im, n...) ). В измеримых терминах ветка, в которой я переписывал вызовы ones в базе, примерно на 5% короче по количеству символов от ones -> fill rewrites. Лучший!

Чтобы получить объективное представление о том, как ones появляется в природе, я собрал все вызовы ones появляющиеся на первых десяти страницах поиска GitHub для ones в коде Джулии, переписал каждый такой вызов соответствующим образом и классифицировал соответствующее изменение (см. эту суть ), а затем сократил данные классификации до следующего резюме:

В анализ было включено 156 звонков по ones . Из тех звонков,

  • 84 звонка (~ 54%) были специальными fill s. (Например, ones(100)/sqrt(100)*7 упрощается до fill(7/sqrt(100), 100) или, еще лучше, fill(.7, 100) . Мне больше всего нравилось kron(0.997, ones(1, J*J*s) -> fill(0.997, 1, J*J*s) .)

  • 3 звонка (~ 2%) были спонтанными трансляциями. (Например, A - ones(n,n) упрощается до A .- 1. .)

  • 5 вызовов (~ 3%) были специальными векторными литералами. (Например, ones(1) упрощается до [1.] .)

  • 1 вызов (~ 0,5%) был семантически конструкцией матрицы мусора. (Хотя этот шаблон относительно редко встречается в природе, этот шаблон довольно часто встречается в test/ потому что у нас нет краткого удобного конструктора для неинициализированных Array s, как, например, <strong i="32">@test_throws</strong> DimensionMismatch BLAS.trsv(...,Vector{elty}(n+1)) сравнению с <strong i="34">@test_throws</strong> DimensionMismatch BLAS.trsv(...,ones(elty,n+1)) .)

Остальные вызовы были семантически разумными, как ones , хотя часто ones видит использование только потому, что оно короткое, а не потому, что one s специально необходимы. Из оставшихся звонков

  • 13 звонков (~ 8%) были немного короче fill . (Например, ones(Int, n, n) -> fill(1, n, n) или ones(Float64, n) -> fill(1., n) .)

  • 50 звонков (~ 32%) были немного длиннее, чем fill . (Например, ones(n, n) -> fill(1., n, n) .)

В целом ~ 60% вызовов ones лучше написаны как-то иначе, ~ 8% семантически разумно ones и немного короче fill , а ~ 32% - разумно семантически ones и немного длиннее fill .

Дополнительное наблюдение:

Я столкнулся только с одним экземпляром вызова ones принимающим аргумент массива, и было неясно, является ли включающий фрагмент реальным кодом. Так что методы ones принимающие аргумент массива, практически не видят, если вообще используют их.

Действительно интересное обсуждение ... перешло от "против" к "за" ... Также в качестве прецедента другого языка R использует rep и matrix способом, который эквивалентен fill (как раз соответствует случаям 1d и 2d), и вы очень быстро к этому привыкнете - хотя я пришел из мира нулей / единиц.

Вау, спасибо @ Sacha0 за приложенные усилия!

Возникает естественный вопрос: а как насчет zeros ? Я предполагаю, что будет значительно больше использования и еще несколько категорий использования (включая такие вещи, как «Я просто не доверяю неинициализированным массивам» или «Я не знаю, как использовать конструкторы Array ").

По какой-то причине (я предполагаю, что это симметрия one и zero ), меня несколько привлекает замена либо ones и zeros на fill или ни то, ни другое.

Проблема с нулями в том, что вы можете оказаться в одной из следующих ситуаций:

  1. Вам нужно перезаписать большинство нулей - в этом случае вам лучше использовать понимание;
  2. Вам не нужно заменять большинство нулей - в этом случае лучше использовать разреженную матрицу;
  3. Вам действительно нужна матрица со всеми нулями - в этом случае вам лучше использовать 0I .

На самом деле нет такого варианта использования, в котором на самом деле выделение матрицы плотных нулей было бы хорошей идеей.

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

Достаточно справедливо - иногда вам наплевать на плотность, а простота того стоит.

Сортировка: решено, что мы сохраняем только полностью неуниверсальные методы, то есть zeros(dims...) и ones(dims...) и, возможно, также zeros(dims) и ones(dims) .

@StefanKarpinski для рекомендаций по использованию означает ли это, что мы рекомендуем zeros(3, 3) over fill(0.0, 3, 3) для обычного кода (когда требуется плотный массив и т. Д.)? Некоторые детали эффективности и т. Д. Мне недоступны, я просто думаю о том, как я буду преподавать лучшие / идиоматические практики в продвижении Джулии.

Это решение кажется мне очень неожиданным, оно не так часто встречается в базе, чтобы специально предотвращать универсальность. В чем причина? это то, что эта функция исходит из Matlab, где она не является универсальной (и работает только с числами с плавающей запятой)?

Сортировка: решено, что мы сохраняем только полностью неуниверсальные методы

В чем причина?

Кроме того, связана ли эта проблема как-то с общей проблемой построения массивов, которая, кажется, рассматривается прямо сейчас, и если да, то как это помогает? Или мы пытаемся сократить количество экспортируемых методов с Base ? (Или что-то совсем другое?)

Запись будет на https://github.com/JuliaLang/julia/issues/24595 :). Лучший!

OP 24595 подробно описал эту проблему в более широком контексте, и теперь продолжение в # 24595 конкретно касается конструкторов удобства ones , zeros и fill подробно. Чтение первого полезно для понимания второго. Лучший!

Что ж, лучше сэкономить хотя бы случай Float64 чем ничего.
Я считаю, что случай целочисленных нулей также весьма актуален, что, как я полагаю, в основном является той мыслью, которую здесь высказал @vtjnash .

Также следует отметить, что zeros не имеет "проблемы" разрешения анти-шаблона 3 * ones(n) . На самом деле, я действительно не понимаю, почему ones и zeros должны идти вместе, за исключением того, что они являются удобными конструкторами в широком смысле. Между этими двумя понятиями нет реальной «симметрии».

Пара дополнительных комментариев к статистическому анализу , поскольку он, по-видимому, лежит в основе следующих обсуждений и рецензии на № 24595. Во-первых, десяти страниц недостаточно для подробных выводов о том, что происходит в дикой природе, они могут дать в лучшем случае приблизительное представление. Некоторые файлы пришли прямо из Matlab, например, как видно из их имени / стиля. Во-вторых, как я подозревал, даже этот анализ показывает, что примерно половина использований ones была «законной». В-третьих, просмотр такого кода ничего не говорит о том, что запись 3 * ones(...) на самом деле является анти-шаблоном, который создает проблемы с производительностью, или это фрагмент кода, который вообще не влияет на производительность (и автор может решили, что это просто более читабельно, так написано - что, как я твердо убежден, никто другой не должен решать в этом случае).

Что касается последнего пункта, и я думаю, что более важно, то, что вы видите при поиске на github, никогда не будет принимать во внимание то, что происходит в REPL людей, выполняющих исследовательскую / предварительную работу в Julia. Это именно то место, где удобные функции наиболее удобны, и, соответственно, их удаление по непонятной причине вызывает наибольшее раздражение. Я хочу сказать, что наличие последовательного набора ортогональных примитивов, позволяющих писать обобщенный и эффективный код, - это великая цель, и усилия по ее достижению действительно достойны похвалы; просто не весь код должен быть красивым, универсальным, компонуемым библиотечным кодом. Всего два цента.

Касательно

Я считаю, что случай целочисленных нулей также весьма актуален, что, как я полагаю, является основной темой, которую здесь высказал @vtjnash .

который относится к

В моей работе над компиляторами и другими структурами данных нередко требуется нулевая инициализированная коллекция. Может быть, они часто редкие, но компактное их представление не стоит того, чтобы снижать производительность.

Обратите внимание, что fill в этом случае также или лучше: fill(0, shape...) против zeros(Int, shape...) .

Что касается других пунктов, возможно, стоит прочитать # 24595 :). Лучший!

Я просто нахожу, что zeros(Int, 10, 10) более читабельно / явно, чем fill(0, 10, 10) , а zeros(T, k) лучше, чем fill(zero(T), k) . Почему мы не можем просто иметь и то, и другое? Я не верю аргументу, что zeros страдает той же проблемой двусмысленности, что и ones .

Что касается других ваших замечаний, возможно, стоит прочитать # 24595

Я читал это. (Я даже связал это.)

Я читал это. (Я даже связал это.)

Прочитав # 24595 полностью и внимательно изучив его, вы понимаете, что # 24595: (1) касается гораздо более широкого вопроса, в котором удобные конструкторы являются лишь частью; и (2) рассматривает гораздо больше, чем просто статистический анализ, опубликованный выше, и вопросы, на которых вы здесь сосредоточены.

Я ценю, что вы сильно относитесь к ones и zeros ; Ваше мнение стало ясно и ясно :). Таким образом, есть вероятность, что наша пропускная способность будет лучше потрачена на продвижение других направлений, чем на продолжение этого разговора в его нынешней форме. Лучший!

Есть ли общий входящий fill вместе с изменением zeros ? zeros имел вполне законное применение для общего программирования, так как это намного безопаснее, чем similar , поэтому весь DiffEq, а потом я знаю, недавно Optim и NLsolve перешли от распределения с similar к zeros поскольку знание того, что все выделено так, чтобы в нем были нули, предотвращает множество ошибок. Однако теперь кажется, что не будет никакого метода для:

zeros(X)

больше, кроме:

similar(X); fill!(X,0)

потому что текущий fill строит только Array s и, следовательно, не соответствует типу, как similar или zeros . Я знаю, что некоторые люди неправильно использовали zeros для выделения, когда им не следовало этого делать, но выделение с помощью zeros во многих случаях является очень разумным поступком. Я надеюсь, что будет добавлено сокращение fill(0,X) чтобы заполнить этот пробел.

Большое спасибо за вдумчивый пост, Крис! :) Может ли zero(X) в качестве временной замены сокращать свое значение?

Обратите внимание, что именно в таких случаях неоднозначность в zeros и ones может быть проблематичной: дает ли zeros(X) объект с eltype(X) и заполненный eltype(X) Аддитивная идентичность fill!(similar(X), 0) ), или вместо этого объект, заполненный мультипликативным нулем для eltype(X) (возможно, не eltype(X) )? (Для расширения см. # 24595.)

Концепция fill(0, X) немного обсуждается в # 11557, и я согласен, что это может быть полезным обобщением fill . Спасибо и всего наилучшего!

Другая проблема заключается в том, что массивы с нестандартными индексами могут быть созданы с помощью чего-то вроде zeros(inds...) (поскольку тип индекса определяет тип массива ). Но для одномерного случая, является ли X "массивом, на который вы хотите быть похожим" или "индексами для желаемого массива"? (В конце концов, AbstractUnitRange <: AbstractArray .) Конкретно, zeros(3:5) означает fill!(similar(3:5), 0) или fill!(OffsetArray(Vector{Float64}(3), 3:5), 0) ?

Ссылка на https://github.com/JuliaLang/julia/pull/24656 , который содержит дополнительное обсуждение удобных конструкторов {ones|zeros }(A::AbstractArray, ...) . Лучший!

Я удивлен, что использование zeros для создания переменных кеша в соответствии с # 24656 считается странным. Я бы подумал, что, если бы zeros было сокращено до почти нулевых накладных расходов, почти в любом случае, когда люди используют similar вместо этого должно быть zeros поскольку это имеет тенденцию исправлять довольно много ошибки. Я думаю, мы должны поощрять больше людей делать это, поскольку similar может быть довольно небезопасным, а отсутствие функции и вместо этого объединение fill! + similar делает менее очевидным, что что люди должны делать. Вот комментарий об этом появлении в Optim.jl:

https://github.com/JuliaNLSolvers/NLsolve.jl/issues/89#issuecomment -294585960

Однако я согласен с @timholy в том, что неочевидно, как это следует интерпретировать. Позвольте мне указать вам на действительно непростой пример в DiffEq.

https://github.com/JuliaDiffEq/MultiScaleArrays.jl

MultiScaleArrays.jl создал абстрактные массивы, которые представляют собой рекурсивные графоподобные структуры, которые можно использовать в решателях difffeq (и я думаю, что теперь он может быть совместим с Optim.jl и NLsolve.jl?). Помимо прочего, это удобство для биологических моделей. Когда мы бросаем его в решатель ODE, возникает вопрос: что нам делать кеш-массивами? В некоторых случаях важно, чтобы пользователь получил обратно нужный массив, поскольку он будет отображаться в их функции ODE f(t,u,du) и они захотят обработать его соответствующим образом. Однако в других случаях это проявляется только как то, против чего транслируется внутреннее вещание. Итак, есть два разных типа переменных кеша.

Чтобы справиться с этим, взгляните на кеш для одного из алгоритмов:

https://github.com/JuliaDiffEq/OrdinaryDiffEq.jl/blob/master/src/caches/low_order_rk_caches.jl#L224 -L234

Здесь rate_prototype = similar(u,first(u)/t,indices(u) аналогичен, но с потенциально другим eltype для единиц. Но обратите внимание, что здесь есть два отдельных типа: similar(u) vs similar(u,indices(u)) . Я интерпретировал их как означающие «соответствовать типу и форме» vs «соответствовать форме и типу eltype, но не обязательно быть одного и того же типа». Таким образом, для AbstractMultiScaleArray первый создаст еще один AbstractMultiScaleArray а другой, для скорости, поскольку он не виден пользователю, просто создаст Array соответствующего размер. Затем они расширяются до similar(u,T) и similar(u,T,indices(u)) .

Может быть, это просто отбрасывание того, что уже существует, но я думаю, что это важное различие. При выполнении общего программирования у вас есть два отдельных кэша: кеш-память, ориентированная на пользователя, для которой вы хотите сопоставить типы в соответствии с их ожиданиями, и внутренние кеши, которые просто используются алгоритмом, и вам нужна как можно большая скорость.

Обратите внимание, что они используют similar потому что мне было лень придумать его версию zeros . На самом деле у меня есть отдельное место, где можно обнулить некоторые из этих массивов, потому что, если пользователь устанавливает только du в своих вычислениях производной f(t,u,du) , они склонны неявно указывать, что "то, что я не установил, означает ноль ", что верно только тогда, когда он был выделен с помощью zeros , поэтому я стараюсь предварительно выделить с помощью zeros как можно больше (та же проблема возникает в NLsolve.jl для этого) .

Надеюсь, это объяснение не было слишком запутанным, чтобы следовать ему. Во всех моих случаях я могу просто переключиться на similar а затем на fill! , но я знаю, что некоторые пакеты этого не сделают, и это будет источником ошибок.

Интересно. Вы правы, что similar(A, inds) может создать другой тип, чем similar(A) , но в целом я всегда думал, что он может создать тот же тип, но с разными индексами. Например, если вам нужен одномерный кеш для операции по столбцам с двумерным объектом, я бы использовал similar(A, first(inds)) . (Конечно , это другой тип , потому что размерность является параметром типа, но это может быть тем же абстрактным типом контейнера.) Вы можете также использовать его для создания кэша 5x5 небольшой плитки и т.д.

В целом это кажется сложной проблемой. Это немного поздно, но стоит ли нам вводить same ? У него могут быть те же аргументы, что и у similar , но в контракте будет требоваться возврат того же абстрактного контейнера.

Я мог бы поддерживать форму с одним аргументом same , но даже это сложно - обратите внимание, что same(a) не может вернуть тот же тип массива, если a не поддерживает setindex! потому что same и similar полезны только в том случае, если вы собираетесь записывать в массив впоследствии. Мы могли бы сделать это ошибкой для неизменяемого a , но в качестве интерфейса для AbstractArray это кажется ненужным (и, возможно, бесполезным) для создания правильного универсального кода.

Точно так же мы не можем предположить, что каждый AbstractArray может поддерживать разные типы или индексы. Для меня наличие формы с двумя или тремя аргументами same просто привело бы к ошибкам времени выполнения в кучу мест, давая людям ложное ощущение безопасности, что их общий код будет хорошо работать для любого AbstractArray input, если это не так.

Но для 1-мерного случая, является ли X «массивом, на который вы хотите быть похожим» или «индексами для желаемого массива»?

Это еще одна причина, по которой я предпочитаю, чтобы keys возвращал контейнер с идентичными индексами и значениями, а затем делал это требованием similar (если вы не указали одно (некоторые) целые числа в в этом случае предполагается Base.OneTo (CartesianRange)).

Это обсуждение смещается к # 18161, и, возможно, следует продолжить там :).

Последняя сортировка склонялась к тому, чтобы просто оставить ones .

Больно ли их держать? Думаю, это помогает людям, пришедшим из тупицы, чувствовать себя в Джулии как дома.

Закрытие, поскольку мы сохраняем ones и zeros .

Была ли эта страница полезной?
0 / 5 - 0 рейтинги