Создайте генератор, который возвращает 2 значения сразу в Джулии

Учитывая генератор:

myVec1 = rand(0:4, 2)
myVec2 = rand(0:4, 8)

myGen = (val1 + val2 for val1 in myVec1, val2 in myVec2)

Так что это в основном матрица с 2 столбцами.
Это можно увидеть, используя collect(myGen).

Как я могу создать генератор, который выдает 2 значения за вызов (в основном столбец)?

Концептуально, что-то эквивалентное:

for myCol in eachcol(collect(myGen))
    @show myCol;
end

Просто без явного выделения матрицы.

Могу ли я обернуть myGen для следующего случая:

for value1, value2 in myGen
  dosomethingelse1(value1, value2)
end

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

Обновление 1

Я уточнил, что означает 2 значения. Итак, в основном мы создаем «2D-массив» в генераторе, и я хотел бы получить доступ ко всему срезу сразу. Я мог бы сделать это с помощью eachcol и eachrow для реального массива, но как насчет генератора?

Обновление 2

Вот тестовый пример:

myVec1 = rand(0:4, 2);
myVec2 = rand(0:4, 800);

@btime begin
    myMat = [val1 + val2 for val1 in myVec1, val2 in myVec2];
    outVec = [sum(myCol) for myCol in eachcol(myMat)];
end

@btime begin
    myGen = (val1 + val2 for val1 in myVec1, val2 in myVec2);
    outVec = [sum(myCol) for myCol in Iterators.partition(myGen, 2)];
end

Решение @Bogumil Kamiński действительно работает, но на практике по какой-то причине оно создает больше выделений, а мотивация заключалась в том, чтобы их уменьшить.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
0
87
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы в основном пропускаете скобки при деструктурировании кортежа значений во втором цикле. Чтобы быть более подробным, вы можете просто вернуть два значения (кортеж) в свою функцию dosomething. Например:

function dosomething(element)
    secondElement = element^2
    element, secondElement
end

И затем вы можете использовать цикл, деструктурируя возвращаемое значение, например:

for (value1, value2) in myGen
    dosomethingelse(value1, value2)
end

Если вам нужен полный рабочий пример:

myArray = [1, 2, 3]

function dosomething(element)
    secondElement = element^2
    element, secondElement
end

myGen = (dosomething(myElement) for myElement in myArray)

function dosomethingelse(value1, value2)
    println("Value 1: $value1 \nValue 2: $value2 \n")
end

for (value1, value2) in myGen
    dosomethingelse(value1, value2)
end

Я обновил вопрос, чтобы уточнить. Дело не в том, что вызываемая функция возвращает 2 значения, а в том, что генератор работает как 2D.

Eric Johnson 05.04.2022 11:28

Это поддерживается непосредственно синтаксисом Julia. Итерация генераторов кортежей работает так же, как интерактивная обработка атомарных значений.

Например, вы можете попробовать:

for (a,b) in ((x, 3x) for x in 1:4)
    println("a=$a, b=$b")
end

Я обновил вопрос, чтобы уточнить. Дело не в том, что вызываемая функция возвращает 2 значения, а в том, что генератор работает как 2D.

Eric Johnson 05.04.2022 11:27

Вы имеете в виду декартово произведение, которое можно получить с помощью IterTools.product?

Przemyslaw Szufel 05.04.2022 17:56

Я предполагаю, что вы хотите что-то вроде:

julia> for (x1, x2) in Iterators.partition(1:10, 2)
           @show x1, x2
       end
(x1, x2) = (1, 2)
(x1, x2) = (3, 4)
(x1, x2) = (5, 6)
(x1, x2) = (7, 8)
(x1, x2) = (9, 10)

Если это то, что вы хотите, то Iterators.partition — это функция, которую вы можете использовать.

Обновлено: если у вас есть два потока источников, используйте zip:

julia> for (x1, x2) in zip(1:5, 6:10)
           @show x1, x2
       end
(x1, x2) = (1, 6)
(x1, x2) = (2, 7)
(x1, x2) = (3, 8)
(x1, x2) = (4, 9)
(x1, x2) = (5, 10)

Редактировать 2: мое первое решение уже работает для вашего случая:

julia> collect(myGen)
2×8 Matrix{Int64}:
 3  7  5  4  6  3  4  5
 1  5  3  2  4  1  2  3

julia> for (x1, x2) in Iterators.partition(myGen, 2)
           @show x1, x2
       end
(x1, x2) = (3, 1)
(x1, x2) = (7, 5)
(x1, x2) = (5, 3)
(x1, x2) = (4, 2)
(x1, x2) = (6, 4)
(x1, x2) = (3, 1)
(x1, x2) = (4, 2)
(x1, x2) = (5, 3)

Я обновил свой вопрос. Подойдет ли еще?

Eric Johnson 05.04.2022 11:27

Я обновил ответ, но я все еще не на 100%, если это то, что вы хотите. Не могли бы вы поделиться рабочим примером ваших исходных данных и ожидаемым результатом?

Bogumił Kamiński 05.04.2022 11:34

Я создал простой кейс. Вы можете посмотреть на это? Трюк с zip не сработает.

Eric Johnson 05.04.2022 11:46

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

Bogumił Kamiński 05.04.2022 11:55

Действительно работает, но почему-то не уменьшает количество аллокаций. Есть идеи?

Eric Johnson 05.04.2022 13:20

Проблема в том, что ваш генератор myGen не является стабильным с самого начала (вы можете проверить это, обернув свой код в функцию и используя @code_warntype). Если вам нужен быстрый код, используйте вместо этого явный цикл for и не используйте генератор.

Bogumił Kamiński 05.04.2022 15:58

Вы уверены, что это так? См. stackoverflow.com/a/71752338.

Eric Johnson 07.04.2022 19:12

Комментарий, который вы связали, правильный. Однако, похоже, что Iterators.partition не только выделяет - как там было прокомментировано - но и по какой-то причине является нестабильным по типу (и это то, что я видел ранее, и теперь я перепроверил и снова вижу это).

Bogumił Kamiński 07.04.2022 21:52
Ответ принят как подходящий

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

function solution_nested(v1, v2)
    myGen = ((val1 + val2 for val1 in v1) for val2 in v2)
    [sum(myCol) for myCol in myGen]
end

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

Это решение дает ожидаемый результат только одного распределения:

julia> @btime solution_nested(myVec1, myVec2);
  1.856 μs (1 allocation: 6.38 KiB)

Поэтому, хотя это решение не совсем соответствует названию, оно, похоже, соответствует тому, что вы описываете. Мы используем ленивую последовательность ленивых столбцов. Причина того, что Iterators.partition работает медленно и неэффективно с памятью, заключается в том, что он фактически выделяет промежуточные векторы значений в разделе: https://github.com/JuliaLang/julia/blob/dacf9d65aff4668b8fff25957d9aaa2cf03868c8/base/iterators.jl#L1232.

Является ли единственное распределение в вашем решении распределением вывода?

Eric Johnson 07.04.2022 19:11

Другие вопросы по теме