List processing to understand with pictures --java8 stream / javaslang --bonus

List processing understood by pictures --java8 stream / javaslang- Extra article!

It was too long as usual, so I separated it.

Let's go roughly!

[Bonus-java8 stream]: Other reduce

There are three definitions of reduce in java8 stream,

I will introduce the other two.

The first is this.

Optional<T> reduce(BinaryOperator<T> accumulator);

Optional<T> reduce(     ((T, T) -> T) f);            //Simplification
         T  reduce(T t, ((T, T) -> T) f);            //Repost of the first reduce

The difference from before is that there is no T t and the return is wrapped in ʻOptional`.

You can see why this happens by looking at the picture.

Those who have the first T t reduce with zero a.png

Those who do not have T t reduce with head.png

The difference between specifying the initial value by yourself and using the first one as the initial value is the difference in the presence or absence of T t.

So, you can see why the return is ʻOptional` by considering the case of an empty list.

If the list is empty, T t can be the final result reduce with zero empty.png

Nothing happens if there is no T t and the list is empty reduce with head empty.png

This is because if there is no initial value and the list is empty, a return cannot be prepared.

[Bonus-java8 stream]: Yet another reduce

I will introduce the remaining one.

The second is this.

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

U reduce(U u, ((U, T) -> U) f, ((U, U) -> U) g);    //Simplification

U reduce(U u, ((U, T) -> U) f);                     //For the time being, forget the third argument
T reduce(T t, ((T, T) -> T) f);                     //Repost of the first reduce

If you forget the mysterious third argument, it's very similar to the first reduce.

This means that even if the initial value is different from the list type, it can be folded, and in other languages, this form is the standard.

reduce with zero b.png

So, as for the last 3 arguments, java8 stream considers parallel execution, so it requires a method to merge the results of split execution. To be honest, I want you to hide this.

reduce with zero java8 stream.png (This is really an "image" because I haven't read the internal processing properly)

There may be some resistance to the 3 argument, but if you think that the 3 argument is noise, it is the same as other languages, so it is good to learn it without avoiding it.

[Bonus-java8 stream]: Application of reduce

If you want to know the sum of [10, 30, 45, 15], you should use sum. That's right.

reduce is only valuable if the initial types are different. If you can use a different type for the initial value, you can actually do it.

For example, you can quickly write the process of checking if the list of ( and ) is closed correctly with reduce.

resolve(asList('(', '(', ')', ')'))   // true

resolve(asList('(', '(', ')'))        // false

resolve(asList('(', ')', ')', '('))   // false

private static boolean resolve(List<Character> cs) {
    return cs.stream()
            .reduce(
                    Optional.of(0),
                    (acc, c) -> c == '('
                            ? acc.map(n -> n + 1)
                            : acc.map(n -> n - 1).filter(n -> n >= 0),
                    (acc1, acc2) -> acc1
                            .flatMap(v1 -> acc2.map(v2 -> v1 + v2))
            ).equals(Optional.of(0));
}

Set the initial value to ʻOptional (0), and while convolving, set .map (n + 1) to ʻOptional (n) for (, and .map to .map (n) for) . Set to (n -1) . However, if it falls below ʻOptional (0), it will be ʻempty. Once it becomes ʻempty, even if you do ʻempty.map (n + 1), it will never return to ʻOptional (0)`.

If it is ʻOptional (0)at the end after folding, it means that the number of(and)is the same, and there was never too much)`.

The third argument that merges two ʻOptionals can be added by looking inside if both are ʻOptional (n). If both ~ ~, that is flatMap. It feels good if this comes out soon.

U reduce(U u, ((U, T) -> U) f, ((U, U) -> U) g);    //Repost

Let's learn while staring at the pattern.

[Bonus-java8 stream]: When the initial value of reduce is used

Since reduce has to be careful about the order and direction of calculation, I will give a quick example.

The two have the same result.

Stream.of(1, 2, 3).reduce((acc, n) -> acc + n).orElse(0)    // 6
Stream.of(1, 2, 3).reduce(0, (acc, n) -> acc + n)           // 6

But the two results are different.

Stream.of("1", "ok", "15:10").reduce((acc, s) -> acc + " | " + s).orElse("")    // 1 | ok | 15:10
Stream.of("1", "ok", "15:10").reduce("", (acc, s) -> acc + " | " + s)           //  | 1 | ok | 15:10

You can understand the difference by drawing a picture yourself.

Good to remember.

[Bonus-javaslang]: Reduce direction

Now that we've touched on reduce, let's take a look at javaslang's reduce.

List.of("1", "ok", "15:10").reduceRightOption((s, acc) -> acc + " | " + s).getOrElse("");    // 15:10 | ok | 1

Reduce, which folds from the right, is also available depending on the language and library. (Note that this is not (acc, s)-> but (s, acc)->)

Of course, even if you use this, the result will often be different from the case from the left, so be careful.

This is the end of reduce!

[Bonus-javaslang]: takeWhile

Introducing takeWhile, which is useful to remember by list operation. This is introduced using javaslang. (I didn't know that java8 stream doesn't have takeWhile ...)

Let's put the label: millisec line in the first subject, in order from the earliest, to less than 30.

lines
    .filter(line -> line.contains(":"))
    .map(line -> Integer.valueOf(line.split(":")[1]))
    .sorted()
    .takeWhile(n -> n < 30);                           // [10, 15]

takeWhile is like" picking up from the beginning only while certain conditions are met. " I use it a lot with dropWhile, so it's good to remember this too.

take while.png

[Bonus-javaslang]: zip

Lastly, I will introduce another one, zip, which I use a lot unexpectedly. This is introduced using javaslang.

It's like pairing the same part of two lists. It may be easier to understand if you think it feels like a zipper.

List.of(1, 2, 3)
    .zip(List.of('a', 'b', 'c'))    // [(1, a), (2, b), (3, c)]
zip.png (`Zip` cannot be used unless there is a type that can pair different types such as` Tuple `.)

Other than when you want to pair ʻA and B, for example, when you want to know the size of each gap in the list of ʻInteger, try zip by shifting the same list by one. There is a way.

List<Integer> times = List.of(10, 15, 35, 60); //this[15 - 10, 35 - 15, 60 - 35]Want to

times
    .zip(times.subSequence(1))
    .map(t -> t._2 - t._1);                    // [5, 20, 25]

Do you use reduce when you think about processing from the beginning? You might think, but if you make a picture, it's completely different. (Reduce does not calculate with the neighbor, but calculates the sum (or) up to that point one by one, so the gap calculation cannot be straightforward.)

zip resolve.png

If you do filter and map well on a log file, zip, take the processing time difference for each line, and try reverse sort and takeWhile, the lines that take 500 ms or more are displayed in order of slowness. You can do it.

[Bonus-java8 stream / javaslang]: Benefits of map

There are definitely talks such as "I don't know the merits" and "Isn't it okay with for?", But of course there are merits that are worth the learning cost.

There are some, but I'll briefly list three.

1. You can separate the situation from what you want to do

For example, there is such a function of String-> Integer.

private static Integer toIntAndTwice(String s) {
    return Integer.valueOf(s) * 2;
}

The code that applies this function to "when there are multiple Strings "," when there is at most one String", and "when there is a String that may be malformed "is Become.

List example

List<Integer> result = List.empty();

List<String> org = ...; // List.of(1, 2, 3) or empty

for (String x : org) {
    result.append(toIntAndTwice(x));
}

return result;

ʻOption` example

Option<Integer> result;

Option<String> org = ...; // Option.of(1) or empty

if (org.isEmpty()) {
    result = Option.none();
} else {
    result = Option.of(toIntAndTwice(org.get()));
}

return result;

try example

Integer result;

String org = ...; // "1" or "x"

try {
    result = toIntAndTwice(org);
} catch (Throwable t) {
    result = null;
}

return result;

You have to write a completely different code to apply toIntAndTwice to String in a particular situation.

If you write this with map, it will be like this.

List example

List<String> org = ...;
List<Integer> mapped = org.map(JSMain::toIntAndTwice);
return mapped;

ʻOption` example

Option<String> org = ...;
Option<Integer> mapped = org.map(JSMain::toIntAndTwice);
return mapped;

Try example

Try<String> org = ...;
Try<Integer> mapped = org.map(JSMain::toIntAndTwice);
return mapped;

It looks the same! This is because "the rules of List and ʻOption in a specific situation" and "what you actually want to do ( toIntAndTwice`) "are separated, and the former is followed by the language.

By the way, if the code is similar so far, I feel that it can be made more common, right? Since List and ʻOption of javaslang inherit from Value`, you can also do this.

If you define such Value <T>-> Value <R>,

private static Value<Integer> mapAnyType(Value<String> org) {
    return org.map(JSMain::toIntAndTwice);
}

It works whether the argument is List or ʻOption`!

List example

Value<Integer> m1 = mapAnyType(List.of("1", "2", "3")); // List(2, 4, 6)

ʻOption` example

Value<Integer> m2 = mapAnyType(Option.none());          // None

Try example

Value<Integer> m3 = mapAnyType(Try.of(() -> "x"));      // Failure(java.lang.NumberFormatException: For input string: "x")

In this case, the same code can handle different situations, so for example, prepare a cancellation process for paid options, and have a" function to cancel all at once (List)" and a "function to cancel if you have one (ʻOption". ")" And "The function to cancel because you should have it ( Try) "can be realized at once.

I think the biggest merit is the separation of "situation" and "processing".

2. Temporary variables do not appear

Legacy code that everyone loves. This is common.

//Initialization
result = 0;
flag = false;

for (i ...) {
    result = ...;

    //Is ~ ~ finished?
    if (i < ...) {
        result = ...;
        flag = true;
    }

    //If it is ~~, it ends
    if (flag) {
        return result;
    }

    //Then loop with ~ ~
    for (j ...) {
        result = ...;

        //If it is ~~, it ends
        if (j < ...) {
            return result;
        }
    }
    //Initialization
    flag = false;
}

//return
return result;

The return result; in this code is the same as the text, but the contents are completely different. (Maybe, I don't even know.)

Since there is a context in the lines, you can not copy and paste only that line, and it seems that you are making blocks with comments, but in reality this is only one huge block.

If this looks like the code above, all three lines of the argument work independently, and there are no variables in the method scope that shouldn't be return. (Since there is one ;, this code is one line. Therefore, there can be no gap state.)

return cs.stream()
        .reduce(
                Optional.of(0),
                (acc, c) -> c == '(' ? acc.map(n -> n + 1) : acc.map(n -> n - 1).filter(n -> n >= 0),
                (acc1, acc2) -> acc1.flatMap(v1 -> acc2.map(v2 -> v1 + v2))
        ).equals(Optional.of(0));

I think that this is overwhelmingly more reusable and of higher quality. (Of course, if you do it with shared code, I think it's better to be a little more careful. The second argument should be named properly and the test code should be written lightly. I think the 3rd edition also said that.)

3. Utilize the experience of another language

I will touch on the details next, but the more you know the idea itself, the better it will be at first glance unless it is a special language.

If you learn java8 stream and go to ruby, you will be able to operate the list immediately, and even if you are new to java8, if you are experienced with python, you will be able to stream.

[Bonus]: Wordbook

As mentioned in the last-minute benefits, most languages have map, filter, reduce.

When you have to maintain a language you don't know in a hurry at work, or when you want to make some changes to the tools you picked up, all you need to know is the words and the processing image. So, at the end, I will end with a summary of how to do the same processing in the language you often hear.

(The part with (*) feels like you can do something similar if you use it well)

lang map filter reduce(zero / left)
reduce(head / left)
reduce(zero / right)
reduce(head / right)
take while zip
java map filter reduce
reduce
-
-
- -
groovy collect findAll inject
inject
-
-
takeWhile transpose (*)
scala map filter reduceLeft / reduceLeftOption
foldLeft
reduceRight / reduceRightOption
foldRight
takeWhile zip
python map filter reduce
-
-
-
itertools.takewhile zip
php array_map array_filter array_reduce
array_reduce
-
-
- array_map (*)
ruby map / collect select reduce / inject
reduce / inject
-
-
take_while zip
js map filter reduce
reduce
reduceRight
reduceRight
- -
haskell map filter foldl
foldl1
foldr
foldr1
takeWhile zip

If you remember the words around map / collect, filter, reduce / fold / inject, most languages will work.

map vs collect and reduce vs inject may be interesting to look into.

end

If anyone has read this far, thank you.

It was good that the article was in time on the day I declared it. That's it.

Recommended Posts

List processing to understand with pictures --java8 stream / javaslang --bonus
List processing to understand with pictures --java8 stream / javaslang-
[java8] To understand the Stream API
Java8 list conversion with Stream map
[Java] Stream processing
[Java] How to operate List using Stream API
Sample code to convert List to List <String> in Java Stream
Java to play with Function
How to handle exceptions coolly with Java 8 Stream or Optional
I want to make a list with kotlin and java!
Connect to DB with Java
Connect to MySQL 8 with Java
[Java] Introduction to Stream API
Java thread to understand loosely
Convert 2D array to csv format with Java 8 Stream API
[Java 8] Duplicate deletion (& duplicate check) with Stream
[Java] Points to note with Arrays.asList ()
[Introduction to Java] About Stream API
Delegate some Java processing to JavaScript
Dare to challenge Kaggle with Java (1)
[Java] Element existence check with Stream
[Processing × Java] How to use variables
I tried to interact with Java
[Java] Convert 1-to-N List to Map
[Java] How to use List [ArrayList]
Server processing with Java (Introduction part.1)
Surprisingly deep Java list inversion-Stream processing
Basic processing flow of java Stream
Java, arrays to start with beginners
Java 8 ~ Stream API ~ to start now
[Java] Conversion from array to List
[Processing × Java] How to use arrays
Java array / list / stream mutual conversion list
[Java] [ibatis] How to get records of 1-to-N relationship with List <Map <>>
I want to perform Group By processing with Stream (group-by-count, group-by-sum, group-by-max)
Java8 / 9 Beginners: Stream API addiction points and how to deal with them
[Java] Get List / Map elements with Iterator
Data processing using stream API from Java 8
How to compile Java with VsCode & Ant
[Java] How to compare with equals method
Introduction to algorithms with java --Search (depth-first search)
[Processing × Java] How to use the loop
Change List <Optional <T >> to Optional <List <T >> in Java
[Java] Map # merge is hard to understand.
[Processing × Java] How to use the class
[Processing × Java] How to use the function
Easy to trip with Java regular expressions
Introduction to algorithms with java --Search (breadth-first search)
[Java] Various methods to acquire the value stored in List by iterative processing