Skip to content
Snippets Groups Projects
Commit a9f2f95d authored by Colin DAMON's avatar Colin DAMON
Browse files

Merge branch '124-markov-chain' into 'master'

Resolve "Markov chain"

Closes #124

See merge request !94
parents d1e372d0 5e958cca
No related branches found
No related tags found
1 merge request!94Resolve "Markov chain"
......@@ -40,3 +40,4 @@ include:
- local: "/java-diamond/.gitlab-ci.yml"
- local: "/diamond-ts/.gitlab-ci.yml"
- local: "/java-memoizers/.gitlab-ci.yml"
- local: "/markov-chain/.gitlab-ci.yml"
package-markov-chain:
variables:
PROJECT_FOLDER: "markov-chain"
extends: .java
only:
refs:
- master
- merge_requests
changes:
- ".gitlab-common-ci.yml"
- "markov-chain/**/*"
# Markov Chain
Découverte du kata [Markov Chain](https://codingdojo.org/kata/MarkovChain/)
- **Auteurs** : Anthony REY et Colin DAMON
- **Date** : 06/09/2021
- **Langage** : Java
- **Niveau** : Moyen
- **Replay** : [Twitch](https://www.twitch.tv/videos/1140906031)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<version>1.0.0</version>
<groupId>fr.ippon.kata</groupId>
<artifactId>java-parent</artifactId>
<relativePath>../java-parent</relativePath>
</parent>
<version>1.0.0-SNAPSHOT</version>
<artifactId>markov-chain</artifactId>
<name>MarkovChain</name>
<developers>
<developer>
<email>arey@ippon.fr</email>
<name>Anthony REY</name>
</developer>
<developer>
<email>cdamon@ippon.fr</email>
<name>Colin DAMON</name>
</developer>
</developers>
</project>
package fr.ippon.markov;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MarkovChain {
private final Map<String, WordStats> stats = new ConcurrentHashMap<>();
public void learn(String text) {
String[] words = text.split(" ");
for (int i = 0; i < words.length - 1; i++) {
String current = words[i];
String next = words[i + 1];
append(current, next);
}
}
private void append(String current, String next) {
stats
.computeIfAbsent(current.toLowerCase(),
this::newWordStats)
.add(next);
}
private WordStats newWordStats(String dummy) {
return new WordStats();
}
public WordStats stats(String word) {
return stats.getOrDefault(word.toLowerCase(),
new WordStats());
}
public Map<String, WordStats> stats() {
return stats;
}
}
package fr.ippon.markov;
public record Stat(String word, float percentage) {
}
package fr.ippon.markov;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class WordStats {
private final AtomicInteger size = new AtomicInteger();
private final Map<String, AtomicInteger> stats = new ConcurrentHashMap<>();
public void add(String word) {
size.incrementAndGet();
stats.computeIfAbsent(word, key -> new AtomicInteger())
.incrementAndGet();
}
public Collection<Stat> get() {
return stats.entrySet()
.stream()
.map(entry -> new Stat(entry.getKey(),
entry.getValue()
.floatValue() / size.floatValue()))
.toList();
}
}
package fr.ippon.markov;
import static org.assertj.core.api.Assertions.*;
import java.util.Map;
import org.junit.jupiter.api.Test;
class MarkovChainTest {
private final MarkovChain markov = new MarkovChain();
@Test
void shouldGetStatsForOneWord() {
markov.learn("Les");
assertThat(markov.stats("Les")
.get()).isEmpty();
}
@Test
void shoulGetStatsForTwoWords() {
markov.learn("Les hommes");
assertThat(markov.stats("Les")
.get()).containsExactly(stat("hommes", 1f));
}
@Test
void shoulGetStatsWithMultipleFollowers() {
markov.learn("Les hommes les plus grands");
assertThat(markov.stats("Les")
.get()).containsExactly(stat("hommes", 0.5f),
new Stat("plus", 0.5f));
assertThat(markov.stats("hommes")
.get()).containsExactly(stat("les", 1f));
}
@Test
void shoulGetStats() {
markov.learn("Les hommes les plus grands");
assertThatStats(markov.stats()).hasWord("les")
.withStat("hommes", 0.5f)
.withStat("plus", 0.5f)
.and()
.hasWord("hommes")
.withStat("les", 1f);
}
private static Stat stat(String word, float stat) {
return new Stat(word, stat);
}
private static StatsAsserter assertThatStats(
Map<String, WordStats> stats) {
return new StatsAsserter(stats);
}
private static class StatsAsserter {
private final Map<String, WordStats> stats;
public StatsAsserter(Map<String, WordStats> stats) {
this.stats = stats;
}
public WordStatAsserter hasWord(String word) {
assertThat(stats).containsKey(word);
return new WordStatAsserter(stats.get(word), this);
}
}
private static class WordStatAsserter {
private final WordStats wordStats;
private final StatsAsserter source;
public WordStatAsserter(WordStats wordStats,
StatsAsserter source) {
this.wordStats = wordStats;
this.source = source;
}
public WordStatAsserter withStat(String word,
float percent) {
float result = wordStats.get()
.stream()
.filter(stat -> stat.word()
.equals(word))
.findFirst()
.orElseThrow(AssertionError::new)
.percentage();
assertThat(result).isEqualTo(percent);
return this;
}
public StatsAsserter and() {
return source;
}
}
}
......@@ -44,6 +44,7 @@ Un kata de code est un petit exercice pensé pour s'entrainer jusqu'à maitriser
- [Tennis Refactoring kata](/tennis/refactoring)
- [Puzzles](java-puzzles)
- [Diamond](java-diamond)
- [Markov chain](markov-chain)
### Énervé
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment