#Can't add code block to lambda?

1 messages · Page 1 of 1 (latest)

nimble tinsel
#

so I was revisiting some code that another user here sent me a few months back for reading a file.

public static List<List<Tile>> readMap(String path) throws IOException {
  try (Stream<String> lines = Files.lines(Path.of("resources/maps/" + path + ".txt"))) {
    return lines.map(s -> SPACE_PATTERN.splitAsStream(s).map(s_ -> new Tile(Integer.valueOf(s_))).toList()).toList();
  }
}```
I wanted to change this to
```java
public static List<List<Tile>> readMap(String path) throws IOException {
  try (Stream<String> lines = Files.lines(Path.of("resources/maps/" + path + ".txt"))) {
    int i, j;
    return lines.map(s -> {
      i++;
      SPACE_PATTERN.splitAsStream(s).map(s_ -> {
        new Tile(Integer.valueOf(s_), i * Main.tileSize, j * Main.tileSize); // 2nd and 3rd paramater is x and y coord
      }).toList();
    }).toList();
  }
}``` but for some reason, even though being pretty much the exact same thing, .map() throws these errors.
Any idea why this may be?
primal spokeBOT
#

<@&987246399047479336> please have a look, thanks.

upbeat rapids
nimble tinsel
#

what am I doing wrong here? it's late and my brain just stopped working properly

amber void
#

i and j are variables of the outer lambda. To reference them in the inner lambda they must be effectively final - which means you can't increment them

#

They may live on that stack-frame, which the compiler must consider, may not exist when the lambda is executed.

upbeat rapids
#

maybe you could use an AtomicInteger instead, but idk if that's the best solution

amber void
#

Sometimes Java's Streams just aren't a good fit.

upbeat rapids
#

streams could have some sort of enumerator adapter like rust iterators

amber void
#

A zip operation on unequal length streams would help in the absence of indexed streams. Probably something we can produce with gatherers (for sequential streams only obviously).

dusty crane
nimble tinsel
#

What if I just use foreach

dusty crane
nimble tinsel
#

Why?

craggy bone
#

its the water a fish (element) swims through. we are defining the water. an iterator is more like "here is a fish, what do you wanna do with it?"

dusty crane
craggy bone
#

could be used the same in hackish ways, but the ideals are waaaay different

dusty crane
craggy bone
#

code against the interface, itll make more sense

dusty crane
#

Rust as an example use its iterator like a stream

craggy bone
#

and a for loop could be used instead of streams too. an iterator and a stream are two separate concepts

dusty crane
#

you could for loop on a stream

#

their idea is closer than you think

craggy bone
#

an iterator is a way simpler idea, it iterates and provides a value. a stream is a set of operations

#

you could provide operations when iterating, just like we do with looping or iterating

#

that doesnt mean a stream is an iterator though

#

i mean, doesnt mean an iterator is a stream (since streams do iterate)

#

its the same concept as network streams

#

its the pipeline the data is sent through

dusty crane
#

map(iterator(list), ...)

#

you can use an iterator like this

craggy bone
#

streams are lazy, iterators are not

dusty crane
#

and iterators are lazy

craggy bone
#

iterators are eager, not lazy. and you are passing an iterator to a map operation

#

so the map operation is what would make it lazy, not the iterator

#

iterators are eager

dusty crane
craggy bone
#

you are passing an iterator to a map. the map interfacea with the iterator to provide lazy operations

dusty crane
#

you can use decorator pattern (or whatever its name is i don't remember) to create iterators like this

craggy bone
#

the iterator isnt lazy, the map operation is

dusty crane
#

the only way to compare it is to use an operation which abuses lazyness like filter, and in such case, results are the same, both lazy

#

wait a minute

#

no you don't even need that

#

iteratpors only iterate when you call next

#

so by definition, they are lazy

#

they do not do things without your consent

#

they won't iterate on the whole array if you don't ask

#
myList.iterator().next()

This will not loop on the whole list, this is the proof that it is lazy

craggy bone
#

you arent making any difference between "obtaining a value to be used" and "specifying a set of operations to be performed"

dusty crane
craggy bone
#

no, iterators can break from iteration

#

which is essentially what you're doing

#

you're accessing the first element, then breaking off the iterator

dusty crane
#

well

#

kinda

#

there is no real way to say that an iterator is lazy or not if you are talking about a vanilla iterators

#

anyway

#

I think we are in the wrong channel pepekek

#

#geek-speak

spiral oxide
#

Iterators are lazy from conceptional point of view

craggy bone
#

sure, these concepts are blurred in some languages like rust. but they are distinct in many other languages, like dart

#

but yeah, #geek-speak is probably a better place to discuss

spiral oxide
#

I think, we have to clarify, what "lazyness" should mean. My definition would be sth like "the related data is computed on demand". Clearly, you have the opportunity to write a finite iterator (e.g. an iterator that iterates over the lines of a text file) in two flavours; eager as well as lazy. But you a not able to create an eager iterator over - lets say - the first billion of primes - if you only have 1MB memory available. But you are able to provide an according lazy implementation. This is why i (also) mean that an iterator is from conceptional point of view lazy.

#

Btw: https://en.wikipedia.org/wiki/Lazy_loading - example ValueHolder - an iterator is nothing but that, except the fact that the method is called "next" instead of "getValue" and moves - as a sideeffect - its internal cursor one place ahead

Lazy loading (also known as asynchronous loading) is a technique used in computer programming, especially web design and web development, to defer initialization of an object until it is needed. It can contribute to efficiency in the program's operation if properly and appropriately used. This makes it ideal in use cases where network content is...

amber void
# nimble tinsel What if I just use foreach

Using Stream.forEach(...) will have the same problem, in that the lambda the forEach receives cannot mutate variables outside the lambda (though it can make mutating changes to objects created outside the lambda, such as an int[] or AtomicInteger.

This is really a case where you are likely best to use Files.newBufferedReader(...) and, for the outer-loop, a traditional for-loop filling an ArrayList (though you can wrap it to make it unmodifiable before returning it). A standard for-loop gives you the counter you want.

The inner loop could pre-split and then use an IntStream.

List<List<Tile>> tiles = new ArrayList<>();
for (var bufferedReader = Files.newBufferedReader(...)) {
    for (int x = 0;; x += Main.tileSize) {
        String s = bufferedReader.readLine();
        if (s == null) {
            break;
        }

        String[] parts = s.split(SPACE_PATTERN);
        List<Tile> row = IntStream.range(0, parts.length)
            .mapToObj(j -> new Tile(Integer.parseInt(parts[j], x, j * Main.tileSize))
            .toList();
        tiles.add(row);
    }
}

return Collections.unmodifiableList(tiles);

An alternative to the BufferedReader is to read all lines into a list (so you know the length) and use the IntStream.mapToObj on the outer loop too - but this does mean the entire file being in memory (and referenced) in memory at the same time as the tiles.

I'd be tempted to avoid the lists of lists and let the program do x/y translations into a flattened array (that is , wrap up the tiles in a type that is the grid). Presumably, once loaded, this grid of tiles has fixed dimensions. To me the Lists don't add much (if any) convenience, but do add overhead.

nimble tinsel
nimble tinsel
#

I started working on this, but before I finish it, how effective is it?

#

looks like something a professional programmer would diss on, idk why

craggy bone
#

you cant use x because it gets modified. but you can introduce a variable like i did above, tempX, which doesn't get modified

amber void
#

Yeah... effective finality required again. The temp can be captured, x cannot. So the issue is differient to the original case where the modification, not the use was happening in the lambda.