

Použila listy s monádou a zažila šok!
source link: https://novotnyr.github.io/scrolls/pouzila-listy-s-monadou-a-zazila-sok/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Použila listy s monádou a zažila šok!
2021/09/25
Odkedy používam zoznamy s monádou, môj život je omnoho lepší!
Už včera sme videli, že škatule a objekty Maybe
zlepšujú zápis alebo predchádzajú chybám s null
! A to všetko vďaka návrhovému vzoru monáda.
Teraz je čas postúpiť ďalej: kým škatule a môžbyť obsahovali vec alebo „žiadnu vec“, ukážme si namonádovaný zoznam, ktorý obsahuje viac položiek!
Monáda potrebuje:
- Dátový typ, ktorý obalí,
- Spôsob, akým obalí veci daného typu,
- Metódu, ktorá vybalí vnútro monády, použije naň funkciu a z nej získa novú zabalenú vec.
Monadický zoznam!
Vyrobme si teraz monadický zoznam prvkov a nazvime ho superzoznam SuperList
, pretože bude omnoho lepší než klasický zoznam!
- obalíme ľubovoľný dátový typ
T
a pripravíme si:- konštruktor, ktorý vie prevziať kolekciu prvkov typu
T
- pomocnú statickú metódu, ktorou vybudujeme superlist na základe viacerých prvkov
- konštruktor, ktorý vie prevziať kolekciu prvkov typu
- pripravíme si zabaľovaco-vybaľovaco-spracovateľskú metódu, ale teraz sa nebude volať
then
, alebind
(bude to lepšie vyzerať).
Metóda bind
Metóda then
, teda bind
bude vyzerať nasledovne:
- pripravíme si prázdny výsledný zoznam prvkov
- nad každým prvkom z aktuálneho superzoznamu zavoláme funkciu, ktorá spočíta údaje a vráti nový superzoznam
- z tohto nového superzoznamu vytiahneme vnútro – teda prvky a prehodíme ich do celkového výsledného zoznamu prvkov
- na konci obalíme výsledný zoznam prvkov do superzoznamu a vrátime ho ako výsledok!
public <R> SuperList<R> bind(Function<T, SuperList<R>> handler) {
List<R> newItems = new ArrayList<>();
for (T item : items) {
SuperList<R> partialSuperList = handler.apply(item);
newItems.addAll(partialSuperList.getItems());
}
return new SuperList<>(newItems);
}
Celý kód
Celý kód obsahuje už len tri veci:
- konštruktor, ktorým vytvoríme superzoznam na základe klasickej kolekcie
- pomocná metóda na vytváranie superzoznamu z prvkov, čo sa hodí v testoch
- a metóda, ktorou získame zo superzoznamu klasický zoznamu
package com.github.novotnyr.monad.list;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
public class SuperList<T> {
private final List<T> items = new ArrayList<>();
public SuperList(Collection<T> entries) {
this.items.addAll(entries);
}
public <R> SuperList<R> bind(Function<T, SuperList<R>> handler) {
List<R> newItems = new ArrayList<>();
for (T item : items) {
SuperList<R> partialSuperList = handler.apply(item);
newItems.addAll(partialSuperList.getItems());
}
return new SuperList<>(newItems);
}
public static <T> SuperList<T> listOf(T... items) {
return new SuperList<>(Arrays.asList(items));
}
public List<T> getItems() {
return this.items;
}
}
Unit test bude vytvorí zoznam troch čísiel, vynásobí ich dvoma a zistí, či to zbehlo v poriadli:
package com.github.novotnyr.monad.list;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import static com.github.novotnyr.monad.list.SuperList.listOf;
class SuperListTest {
@Test
public void testMultiplication() {
List<Integer> doubles = listOf(1, 2, 3)
.bind(n -> listOf(n * 2))
.getItems();
Assertions.assertEquals(Arrays.asList(2, 4, 6), doubles);
}
}
Keďže sme si umne pripravili statickú metódu listOf()
, s použitím statického importu máme celkom pekný zápis!
Pravá zábava: výpočty nad funkciami s viacerými výsledkami
Toto je samozrejme v poriadku, ale pravá zábava nastáva vtedy, ak máme výpočty, ktoré môžu vracať viac hodnôt naraz!
Vymyslime si metódu, ktorá pre číslo n vráti jeho susedov: teda číslo o jedna menšie, samotné číslo a číslo o 1 väčšie.
Inak povedané, vstupom metódy je číslo a výsledkom je trojica čísiel. Keďže máme superzoznam, neváhajme ho použiť!
package com.github.novotnyr.monad.list;
import static com.github.novotnyr.monad.list.SuperList.listOf;
public class Utils {
public static SuperList<Integer> neighbours(int number) {
return listOf(number - 1, number, number + 1);
}
}
Test potom vyzerá nasledovne:
@Test
void testNeighbours() {
List<Integer> neighbours = listOf(1, 2, 3)
.bind(Utils::neighbours)
.getItems();
List<Integer> expected = Arrays.asList(0, 1, 2, 1, 2, 3, 2, 3, 4);
Assertions.assertEquals(expected, neighbours);
}
Vytvorili sme superzoznam troch čísiel, aplikovali naň funkciu neighbours
a výsledkom je .. spľasnutý zoznam deviatich čísiel!
Presnešie povedané, postupne prechádzame zoznam troch čísiel, na každé z nich použijeme neighbours
a výsledné čiastkové superzoznamy zlúčime dohromady do jedného zoznamu.
Postupne to vyzerá nasledovne
1 -> [0, 1, 2]
2 -> [1, 2, 3]
3 -> [2, 3, 4]
Máme teda zoznam troch zoznamov:
[[0, 1, 2], [1, 2, 3], [2, 3, 4]]
Funkcia bind
zoberie tri čiastkové superzoznamy a zlepí ich dohromady do jedného veľkého superzoznamu:
Susedia a duplikácie!
Môžeme si urobiť aj iný test, kde zistíme susedov a každého z nich zduplikujeme!
@Test
void testNeighboursAndDuplicate() {
List<Integer> neighbours = listOf(7)
.bind(Utils::neighbours)
.bind(n -> listOf(n * 2))
.getItems();
List<Integer> expected = Arrays.asList(12, 14, 16);
Assertions.assertEquals(expected, neighbours);
}
Z čísla 7 vzniknú susedia 6, 7 a 8, a keď ich zduplikujeme, očakávame 12, 14 a 16!
Jednoduchší bind - skratka pre prevod prvku na prvok
Metóda bind
očakávala funkciu, ktorá vráti superzoznam, teda monadický zoznam.
Mnohokrát sme však leniví a vieme, že každý prvok zoznamu budeme premieňať na iný prvok zoznamu.
Nebolo by skvelé niečo takéto?
List<Integer> doubles = listOf(1, 2, 3)
.bind(n -> n * 2)
.getItems();
Určite áno!
Našťastie, ak skombinujeme bind
a obaľovaciu funkciu listOf
, vieme si zjednodušiť život.
Dôležité je, že funkcia v parametri už nevracia superzoznam SuperList<R>
, ale len bežný jednoduchý prvok R
.
Iniciálny kód:
public <R> SuperList<R> map(Function<T, R> mapper) {
List<R> newItems = new ArrayList<>();
for (T item : items) {
R partialItem = mapper.apply(item);
newItems.add(partialItem);
}
return new SuperList<>(newItems);
}
Otestujeme to so zjednodušeným zápisom:
@Test
public void testMultiplicationWithMap() {
List<Integer> doubles = listOf(1, 2, 3)
.map(n -> n * 2)
.getItems();
Assertions.assertEquals(Arrays.asList(2, 4, 6), doubles);
}
Namiesto zápisu bind(n -> listOf(n * 2)
už vraciame len jednoduchý dvojnásobok. Funkcia map
sa postará o odbalenie a zabalenie do monádového superzoznamu.
Ak by sme chceli skombinovať bind
a listOf
a máme odvahu skladať funkcie, spravme to:
public <R> SuperList<R> map(Function<T, R> mapper) {
Function<T, SuperList<R>> handler = mapper.andThen(SuperList::listOf);
return bind(handler);
}
Čo sme ukázali?
Náš superzoznam je tretí príklad monády, ktorý ukazuje, že stačí definovať triedu obaľujúcu typ, pridať pár konštruktorov, a metódu then
, resp bind
a vieme robiť kúzelné veci!
Pekné prekvapenie je, že v Jave už superzoznam existuje: stačí na java.util.List
zavolať metódu stream()
a získať prúd java.util.stream.Stream!
- metóda
map
je rovnaká ako naša metódamap
- a metóda
flatMap
je rovnaká ako naša metódabind
.
Názov flatMap
znamená, že prvok premeníme – namapujeme – na iný prvok zoznamu a ak by náhodou tento výsledný prvok predstavoval zoznam, tak ho odbalíme („spľaštíme“) a jeho vnorené prvky vložíme do výsledku bez obalu.
@Test
void testStream() {
List<Integer> neighbours = Stream.of(1, 2, 3)
.flatMap(n -> Stream.of(n - 1, n, n + 1))
.collect(Collectors.toList());
List<Integer> expected = Arrays.asList(0, 1, 2, 1, 2, 3, 2, 3, 4);
Assertions.assertEquals(expected, neighbours);
}
To je dôkaz, že od istej chvíle všetko bude vyzerať ako monáda!
Recommend
-
8
随着抖音电商盘子越做越大,商家、达人、机构服务商对流量获取的需求也越来越高。内容和直播,是抖音平台的两大重要部分,因此内容推广工具和直播间推广工具也应运而生,一个是Dou+,而另一个就是Feed流了。那么,Feed流与Dou+的区别在哪...
-
7
Rust Lab 20 февраля (воскресенье) 12:00 Киев, бульвар Т. Шевченка, 2, Kuznya Rebra ta Pyva
-
4
Воркшоп: пишемо гру на Rust (событие в архиве) 5 — 6 лютого безкоштовно Запрошуємо вас в офлайн, де...
-
8
抖音直播公布“DOU有好戏”计划 作者:亿邦动力 文章来源:亿邦动力 2022-04-14 17:08 4月14日消息,今日下午,在北京梅兰芳大剧院,抖音
-
1
DOU+投放的第一原则,拉长时长 电商热点 2022年4月20日 21:23 15
-
3
Zlepšila som si život prípravkom proti `null`! /home/robonovotny Zlepšila som si život prípravkom proti null! 2021/09...
-
3
Používame Spring Web Services /home/robonovotny Používame Spring Web Services 2009/06/23 package sk...
-
8
记一次失败的StackOverflow回答 有一位同学在StackO...
-
4
Dou+投放18个问题汇总 发布者: 91运营 ...
-
9
Obsah Používání KVM a na vpsFree.cz Tato info stránka obsahuje návody pro zprovoznění KVM na Alpine Linux 3.4+, CentOS 7 a Debian 8. Nejprve je po...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK