LINUX.ORG.RU

Короче есть два итератора

 , ,


1

3

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

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

Поделитесь мыслями, свои закончились.

★★★★★

Последнее исправление: ya-betmen (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA

Нужна парелельная обработка двух итераторов, поэтому предлагаю сделать итератор который возвращает кортеж (Optional<T>, Optional<T>), элемент [0] это который подходит под условие, а [1] который не подходит, и уже потом при итерации по этому кортежу передавать дальше на обработку данные. Ну или как enum { Left<T>, Right<T> }.

Тут зависит от языка, но если итератор может восстанавливаться извне, то вполне получиться представить данные в виде двух виртуальных последовательных массивов для функций которые обрабатывают часть [0] и [1].

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 4)

Отдаёшь два итератора, которые имеют общий лок и доступ к исходному итератору.

Первый итератор возвращает значения, которые удовлетворяют условию, пока не наткнётся на значение, которое не удовлетворяет. В этот момент итератор блокируется, пока второй итератор не вызовется, не пройдёт по значениям, которые не удовлетворяют условию и не наткнётся на значение, которое удовлетворяет. В этот момент первый итератор разблокируется, а второй заблокируется.

Соответственно использовать эти итераторы нужно в двух разных потоках.

Это всё для Java-подобных итераторов, для которых не предусмотрена работа в неблокирующем режиме (т.е. вернуть специальное значение из разряда «попробуйте позже»).

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Да мне независимо от языка, просто я пока не до конца понял чего именно хочу. По идее мне нужно хранить только кусочек между позициями итераторов. Хмм. Надо попробовать.

ya-betmen ★★★★★
() автор топика
Ответ на: комментарий от ya-betmen

нужно хранить только кусочек между позициями итераторов

Зачем два итератора? Идёшь по одному итератору и в зависимости от результата вызова предиката с условием делаешь с очередным элементом то или это.

Nervous ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Там два filter и filterNot, а ОП хочет что бы два итератора как угодно ходили по списку, но два раза список не фильтровали.

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

maxcom ★★★★★
()

Вот ещё один вариант. Конвертируешь Iterator<T> в Iterator<Either<T, T>>, который отдаёт элементы, у которых либо первое, либо второе значение выставлено, в зависимости от условия. Тут трансформация тривиальная.

vbr ★★★★★
()

А проверка настолько тяжелая, что это заметно?

Возможно, ты хочешь БД с индексом по полю

Ну или если это массив словарей, то добавить ещё одно поле с результатом проверки. И обновлять его при обновлении элемента

router ★★★★★
()
Ответ на: комментарий от router

Мне надо наружу итератор скормить, вообще сделать 2 массива руками и скормить их не проблема, просто решил понаяпрягать мозги.

Есть предположение, что тот кто придет следом может запутаться в коде. Но хочется универсальную штуку

ya-betmen ★★★★★
() автор топика
Ответ на: комментарий от ya-betmen

Есть предположение, что тот кто придет следом может запутаться в коде

Ну… Если у тебя есть совесть, то keep it simple. и комментарии

А тежелое костылестроение - если тебе надо, чтобы после тебя никто не разобрался

router ★★★★★
()
Ответ на: комментарий от MOPKOBKA

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

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)
Ответ на: комментарий от peregrine

Если условие фиксированное, то можно переставить элементы в коллекции таким образом, чтобы то что не соответствует условию было с одной стороны списка, а то что соответствует с другой

Не проще ли тогда за тот же самый один проход разложить элементы по условию на две коллекции и потом итерироваться по ним отдельно и независимо?

Nervous ★★★★★
()

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

Nervous ★★★★★
()
Ответ на: комментарий от Nervous

Ситуативно. Потому как может быть необходима и третья итерация по всем объектам. Так-то если условий больше 2 и элементы могут входить в разные группы можно потратив память отдельные списки с указателями на нужные элементы поддерживать для каждого условия. Чисто чтоб быстро работало, а снаружи было простым.

peregrine ★★★★★
()
Ответ на: комментарий от peregrine

можно потратив память отдельные списки с указателями на нужные элементы поддерживать для каждого условия

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

Если условие тяжёлое — можно его мемоизировать.

В общем, без уточнения условий задачи это всё гадание на кофейной гуще.

Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 2)
Ответ на: комментарий от Nervous

Тут всё от ЯП зависит и того насколько и к каким нагрузкам надо оптимизации делать. Есть 2 большие разницы - когда мы каждый такт считаем для какого-нибудь микроконтроллера слабенького/высоконагруженной системы или системы реального времени (их оптимизации тоже разные, но в другом смысле - первым часто можно пропустить что-то если не успевается обсчитать, вторая требует экономии ресурсов серверов, третьей надо чтоб не было временных лагов, когда оно зависает) и когда мы занимаемся по сути преждевременными оптимизациями, по типу ну неплохо, чтоб программа запускалась на 1 секунду быстрее, но когда у нас это игра и 30 секунд читаются текстурки с диска, то как бы 1 секунда ну хорошо, может даже важно, если статистика говорит что именно за эту секунду у большого процента юзеров сгорает жопа и они идут играть в игру конкурентов, но такой паники как в первых 2 случаях нет. Т.е. от ТС-а нужно больше конкретики.

peregrine ★★★★★
()

имхо

на выходе не может быть два выходных равноправных итератора .

ибо пусть есть два выходных итератора( первый из нулей , второй из единиц)

тогда пусть входной итератор отдаёт нули:

тогда чтение из второго итератора завешивает чтеца и переполняет буфер ответов первого итератора

имхо

по сути тебе не итератор нужен а сплитер потоков :)

qulinxao3 ★☆
()
Последнее исправление: qulinxao3 (всего исправлений: 1)

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

alysnix ★★★
()
Ответ на: комментарий от ya-betmen

Да мне независимо от языка, просто я пока не до конца понял чего именно хочу

Задача окружающих - объяснить тебе, что ты хочешь. Твоя задача - строго выполнять советы окружающих.

alysnix ★★★
()
package main

func mkchan[T any](arr []T) <-chan T {
	ch := make(chan T)

	go func() {
		defer close(ch)
		for _, n := range arr {
			ch <- n
		}
	}()

	return ch
}

func sendout[T any](input <-chan T, cond func(v T) bool) (<-chan T, <-chan T) {
	var (
		west = make(chan T)
		east = make(chan T)
	)

	var westclosed, eastclosed bool

	go func() {
		var (
			westread = make(chan bool)
			eastread = make(chan bool)
		)
		defer close(westread)
		defer close(eastread)

		var nwest, neast int

		for n := range input {
			if cond(n) {
				nwest++
				go func() {
					west <- n
					westread <- true
				}()
			} else {
				neast++
				go func() {
					east <- n
					eastread <- true
				}()
			}
		}

		for nwest != 0 || neast != 0 {
			select {
			case <-westread:
				nwest--
			case <-eastread:
				neast--
			}
			if nwest == 0 && !westclosed {
				westclosed = true
				close(west)
			}
			if neast == 0 && !eastclosed {
				eastclosed = true
				close(east)
			}
		}
	}()

	return west, east
}

func main() {
	arr := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	cond := func(n int) bool { return n%2 == 0 }

	west, east := sendout(mkchan(arr), cond)
	for n := range west {
		println(n)
	}
	for n := range east {
		println(n)
	}
}
kaldeon
()
Последнее исправление: kaldeon (всего исправлений: 1)

Если задача в параллельности, то никак - делаешь два и всё, если речь о том, что лень в прямой поэлементной итерации написать if(cond) {} else {}, то зря - идеальный вариант и параллелится идеально, разбавь это функциональщиной для фильтра с else, то будет ещё и красиво.

AKonia ★★★
()