Насколько я понимаю, очередь используется для создания данных на одном конце, а затем другой поток потребляет данные с другого конца.
Очередь представляет собой структуру данных FIFO (First In-First-Out). Он имеет множество применений, помимо связи между потоками.
Мне любопытно узнать причину наличия неизменной очереди.
Если вас сбивает с толку необходимость неизменного что угодно, похоже, вы не понимаете функциональное программирование. Помните, вы сами говорили, что Vavr — это функциональная библиотека, т.е. библиотека для написания функционального кода на Java.
Один из основных принципов функционального программирования заключается в том, что все неизменно.
Это включает в себя очередь. Если вам нужна очередь, то есть коллекция FIFO, для хранения ваших данных, то она тоже должна быть неизменной.
В качестве примера предположим, что вы хотите добавить числа от 1 до 10 в очередь, а затем прочитать из этой очереди и распечатать значения.
В императивном языке программирования, таком как Java, вы должны сделать это следующим образом, используя java.util.Queue
и такую реализацию, как java.util.LinkedList
:
// Build queue with numbers 1-10
Queue<Integer> queue = new LinkedList<>();
for (int i = 1; i <= 10; i++)
queue.offer(i);
// Poll queue and print numbers
for (Integer num; (num = queue.poll()) != null; )
System.out.println(num);
Напротив, функциональное программирование в значительной степени опирается на рекурсивные функции (отсюда функциональное программирование) для таких операций, когда вызов вложенного вызова в стеке имеет разные значения параметров функций.
Помните, что в императивном стиле счетная переменная i
и очередь queue
изменяются во время итерации.
В функциональном стиле они оба должны быть неизменяемыми, поэтому вы делаете это, написав рекурсивную функцию, подобную этой (на Java), используя io.vavr.collection.Queue
:
private static Queue<Integer> build(int i, int end, Queue<Integer> queue) {
if (i > end)
return queue;
return build(i + 1, end, queue.enqueue(i));
}
Затем назовите это:
// Build queue with numbers 1-10
Queue<Integer> queue = build(1, 10, Queue.empty());
Поскольку очередь является неизменной, enqueue()
возвращает новую очередь с добавленным новым значением. Затем эта новая очередь передается в качестве параметра рекурсивного вызова до тех пор, пока она не будет выполнена, после чего последняя очередь, содержащая числа, возвращается обратно в стек вызовов.
Примечание: в функциональном языке, который реализует оптимизацию хвостовой рекурсии (в Java ее нет), функция build()
выше фактически не создает стек вызовов, поэтому она не вызовет переполнения стека. Кроме того, новая очередь, возвращаемая enqueue()
, не копирует все существующие значения, так что это не так дорого, как кажется.
Чтобы затем опросить значения из очереди и распечатать их, вы также должны использовать рекурсивный метод:
private static void print(Queue<Integer> queue) {
if (queue.isEmpty())
return;
Tuple2<Integer,Queue<Integer>> t = queue.dequeue();
System.out.println(t._1());
print(t._2());
}
Здесь dequeue()
< /a> возвращает два значения: значение, удаленное из очереди, и новая очередь с удаленным значением. Затем функция выводит значение и делает рекурсивный вызов для вывода остальной части очереди.
person
Andreas
schedule
15.07.2017