I can create a recursive closure:
static IntUnaryOperator fibo;
fibo =
(i) ->
i<2 ? 1 : fibo.applyAsInt(i-1)+ fibo.applyAsInt(i-2);
But of course, it has sense only as an example. To be useful, such collection should keep already once counted elements and get() them without recounting. The counting of elements should happen in lazy way, at first need. Thus, no member will have to be calculated more than once. In such way we'll a structure that will look like a recursively defined sequence, and will be fast and reusable.
When I started to study Java 8 I thought that Stream works in that way. But it does not, for the stream cannot be used twice.
I thought about the following construction:
IntStream fi;
fi=IntStream.iterate(0, i -> fi[i-1]+fi[i-2]);
But that way it won't work - I can't get an item from the stream by index.The other problem is that if I'll later go along the stream, it will be consumed and I can't use it repeatedly. If I copy the stream to List, it is not lazy anymore.
As a result, I need some construction that I can address by index. As fibo(i)
.
Edit. Obviously, the solution cannot be a stream, for the stream cannot be used twice. I don't want to repeat all calculations on every call to F(i).
Streams are lazy because intermediate operations are not evaluated until terminal operation is invoked. Each intermediate operation creates a new stream, stores the provided operation/function and return the new stream. The pipeline accumulates these newly created streams.
No storage. Streams don't have storage for values; they carry values from a source (which could be a data structure, a generating function, an I/O channel, etc) through a pipeline of computational steps.
It seems you are asking for something like this:
public class Fibonacci extends AbstractList<BigInteger> {
@Override
public Stream<BigInteger> stream() {
return Stream.iterate(new BigInteger[]{ BigInteger.ONE, BigInteger.ONE },
p->new BigInteger[]{ p[1], p[0].add(p[1]) }).map(p -> p[0]);
}
@Override
public Iterator<BigInteger> iterator() {
return stream().iterator();
}
@Override
public int size() {
return Integer.MAX_VALUE;
}
@Override
public BigInteger get(int index) {
return stream().skip(index).findFirst().get();
}
}
It’s accessible via the List
interface (it doesn’t implement RandomAccess
for a good reason), thus, you may ask for the n’th value via get(n)
. Note that the implementation of get
hints how you can get values at positions after Integer.MAX_VALUE
. Just use stream().skip(position).findFirst().get()
.
Beware! This list is infinite, as you asked for. Don’t ask it for things that operate on all elements, e.g. not even toString()
. But things like the following will work smoothly:
System.out.println(new Fibonacci().subList(100, 120));
or
for(BigInteger value: new Fibonacci()) {
System.out.println(value);
if(someCondition()) break;
}
However, when you have to process large sequences of elements and want to do it efficiently, you should ensure to work on the iterator or stream to avoid O(n²)
complexity of repeated get
calls.
Note that I changed the element type to BigInteger
as it would be pointless to think about infinite streams when it comes to the Fibonacci sequence and the int
or long
value type. Even with the long
value type, the sequence is over after only 92 values as then, overflow occurs.
Update: now that you made clear that you are looking for a lazy storage, you may change the class above as follows:
public class Fibonacci extends AbstractList<BigInteger> {
final Map<BigInteger,BigInteger> values=new HashMap<>();
public Fibonacci() {
values.put(BigInteger.ONE, BigInteger.ONE);
values.put(BigInteger.ZERO, BigInteger.ONE);
}
@Override
public BigInteger get(int index) {
return get(BigInteger.valueOf(index));
}
public BigInteger get(BigInteger index) {
return values.computeIfAbsent(index, ix ->
get(ix=ix.subtract(BigInteger.ONE)).add(get(ix.subtract(BigInteger.ONE))));
}
@Override
public Stream<BigInteger> stream() {
return Stream.iterate(BigInteger.ZERO, i->i.add(BigInteger.ONE)).map(this::get);
}
@Override
public Iterator<BigInteger> iterator() {
return stream().iterator();
}
@Override
public int size() {
return Integer.MAX_VALUE;
}
}
I used BigInteger
as key/index here to fulfill the requirement to be (theoretically) infinite, though we can use a long
key as well for all practical uses. The key point is the initially empty storage: (now exemplary using long
):
final Map<Long,BigInteger> values=new HashMap<>();
which is pre-initialized with the values that should end each recursion (unless it ends earlier due to already computed values):
values.put(1L, BigInteger.ONE);
values.put(0L, BigInteger.ONE);
Then, we can ask for a lazily computed value via:
public BigInteger get(long index) {
return values.computeIfAbsent(index, ix -> get(ix-1).add(get(ix-2)));
}
or a stream delegating to the get
method described above:
LongStream.range(0, Long.MAX_VALUE).mapToObj(this::get);
This creates a stream that is only “practically infinite” whereas the complete example class above, using BigInteger
is theoretically infinite…
The Map
will remember every computed value of the sequence.
I cannot think up a good general solution, but if you want to access specifically two previous elements, this could be done in quite easy way defining the custom Spliterator
like this:
public static IntStream iterate(int first, int second, IntBinaryOperator generator) {
Spliterator.OfInt spliterator = new AbstractIntSpliterator(Long.MAX_VALUE,
Spliterator.ORDERED) {
int prev1 = first, prev2 = second;
int pos = 0;
@Override
public boolean tryAdvance(IntConsumer action) {
if(pos < 2) {
action.accept(++pos == 1 ? prev1 : prev2);
} else {
int next = generator.applyAsInt(prev1, prev2);
prev1 = prev2;
prev2 = next;
action.accept(next);
}
return true;
}
};
return StreamSupport.intStream(spliterator, false);
}
Usage:
iterate(1, 1, Integer::sum).limit(20).forEach(System.out::println);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With