Add pattern matching example
This commit is contained in:
		
							
								
								
									
										72
									
								
								lang/src/main/java/patternmatching/Main.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								lang/src/main/java/patternmatching/Main.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
package patternmatching;
 | 
			
		||||
 | 
			
		||||
import java.io.FileNotFoundException;
 | 
			
		||||
import java.io.PrintWriter;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
 | 
			
		||||
public class Main {
 | 
			
		||||
 | 
			
		||||
    private  static class CustomObject {
 | 
			
		||||
 | 
			
		||||
        private int a;
 | 
			
		||||
 | 
			
		||||
        CustomObject(int a) {
 | 
			
		||||
            this.a = a;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public String toString() {
 | 
			
		||||
            return "CustomObject{" +
 | 
			
		||||
                    "a=" + a +
 | 
			
		||||
                    '}';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String formatObject(Object o) {
 | 
			
		||||
        if (o instanceof Integer) return "int " + o;
 | 
			
		||||
        if (o instanceof Byte) return "byte " + o;
 | 
			
		||||
        if (o instanceof Double) return "double " + o;
 | 
			
		||||
        if (o instanceof String) return "string " + o;
 | 
			
		||||
        return String.format("Object %s", o);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static long[] runNTimes(int numberOfRuns, Object[] toTest, Consumer<? super Object> code) {
 | 
			
		||||
        long runs[] = new long[numberOfRuns];
 | 
			
		||||
        for (int i = 0; i < numberOfRuns; i++) {
 | 
			
		||||
            long startTime = System.nanoTime();
 | 
			
		||||
            Arrays.stream(toTest).forEach(code);
 | 
			
		||||
            long endTime = System.nanoTime();
 | 
			
		||||
            long duration = (endTime - startTime);
 | 
			
		||||
            runs[i] = duration;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return runs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void main(String[] args) {
 | 
			
		||||
        Object[] toTest = {"Hello", 1.2, new CustomObject(5)};
 | 
			
		||||
 | 
			
		||||
        final PatternMatcher<Object, String> pm = PatternMatcher
 | 
			
		||||
                .when(Integer.class::isInstance, i -> "int " + i)
 | 
			
		||||
                .orWhen(Byte.class::isInstance, b -> "byte " + b)
 | 
			
		||||
                .orWhen(Double.class::isInstance, d -> "double " + d)
 | 
			
		||||
                .orWhen(String.class::isInstance, s -> "String " + s)
 | 
			
		||||
                .otherwise(o -> "Object " + o);
 | 
			
		||||
 | 
			
		||||
        final int nRuns = 100000;
 | 
			
		||||
        final Statistics formatObjectStats = Statistics.from(runNTimes(nRuns, toTest, Main::formatObject));
 | 
			
		||||
        System.out.println(String.format("Time out of %d runs for formatObject(Object o) : %s", nRuns, formatObjectStats));
 | 
			
		||||
        final Statistics patternMatcherStats = Statistics.from(runNTimes(nRuns, toTest, pm::matches));
 | 
			
		||||
        System.out.println(String.format("Time out of %d runs for patternMatcher : %s", nRuns, patternMatcherStats));
 | 
			
		||||
 | 
			
		||||
        final PrintWriter writer;
 | 
			
		||||
        try {
 | 
			
		||||
            writer = new PrintWriter("/tmp/stats.csv");
 | 
			
		||||
            writer.println(formatObjectStats.asCSv());
 | 
			
		||||
            writer.println(patternMatcherStats.asCSv());
 | 
			
		||||
        } catch (FileNotFoundException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								lang/src/main/java/patternmatching/PatternMatcher.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								lang/src/main/java/patternmatching/PatternMatcher.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
package patternmatching;
 | 
			
		||||
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.function.Predicate;
 | 
			
		||||
 | 
			
		||||
@FunctionalInterface
 | 
			
		||||
public interface PatternMatcher<T, R> {
 | 
			
		||||
 | 
			
		||||
    static <T, R> PatternMatcher<T, R> when(Predicate<T> predicate, Function<T, R> action) {
 | 
			
		||||
        Objects.requireNonNull(predicate);
 | 
			
		||||
        Objects.requireNonNull(action);
 | 
			
		||||
        return value -> predicate.test(value) ? Optional.of(action.apply(value)) : Optional.empty();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default PatternMatcher<T, R> orWhen(Predicate<T> predicate, Function<T, R> action) {
 | 
			
		||||
        Objects.requireNonNull(predicate);
 | 
			
		||||
        Objects.requireNonNull(action);
 | 
			
		||||
        return value -> {
 | 
			
		||||
            final Optional<R> result = matches(value);
 | 
			
		||||
            if (result.isPresent()) return result;
 | 
			
		||||
            if (predicate.test(value)) return Optional.of(action.apply(value));
 | 
			
		||||
            return Optional.empty();
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default PatternMatcher<T, R> otherwise(Function<T, R> action) {
 | 
			
		||||
        Objects.requireNonNull(action);
 | 
			
		||||
        return value -> {
 | 
			
		||||
            final Optional<R> result = matches(value);
 | 
			
		||||
            return result.isPresent() ? result : Optional.of(action.apply(value));
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Optional<R> matches(T value);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								lang/src/main/java/patternmatching/Statistics.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								lang/src/main/java/patternmatching/Statistics.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
package patternmatching;
 | 
			
		||||
 | 
			
		||||
import java.io.FileNotFoundException;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
public final class Statistics {
 | 
			
		||||
 | 
			
		||||
    private final long[] data;
 | 
			
		||||
    private Double mean = null;
 | 
			
		||||
    private Double variance = null;
 | 
			
		||||
 | 
			
		||||
    private Statistics(long[] data) {
 | 
			
		||||
        this.data = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    double getMean() {
 | 
			
		||||
        if (mean == null) {
 | 
			
		||||
            mean = (double) Arrays.stream(data).sum() / data.length;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return mean;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    double getVariance() {
 | 
			
		||||
        if (variance == null) {
 | 
			
		||||
            final double mean = getMean();
 | 
			
		||||
            double temp = 0;
 | 
			
		||||
            for(double a : data) {
 | 
			
		||||
                temp += (a-mean)*(a-mean);
 | 
			
		||||
            }
 | 
			
		||||
            variance = temp/(data.length-1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return variance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    double getStddev() {
 | 
			
		||||
        return Math.sqrt(getVariance());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    double getMedian() {
 | 
			
		||||
        Arrays.sort(data);
 | 
			
		||||
        if (data.length % 2 == 0) return (data[(data.length / 2) - 1] + data[data.length / 2]) / 2.0;
 | 
			
		||||
        return data[data.length / 2];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static Statistics from(long[] data) {
 | 
			
		||||
        return new Statistics(data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "mean=%.2fms stddev=%.2fms median=%.2fms",
 | 
			
		||||
                getMean()/1000,
 | 
			
		||||
                getStddev()/1000,
 | 
			
		||||
                getMedian()/1000
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String asCSv() {
 | 
			
		||||
        final String csv = Arrays.stream(data).mapToObj(String::valueOf).collect(Collectors.joining(","));
 | 
			
		||||
        return csv;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								lang/src/test/java/patternmatching/PatternMatcherTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								lang/src/test/java/patternmatching/PatternMatcherTest.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
package patternmatching;
 | 
			
		||||
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
 | 
			
		||||
import static org.hamcrest.CoreMatchers.is;
 | 
			
		||||
import static org.junit.Assert.*;
 | 
			
		||||
 | 
			
		||||
public class PatternMatcherTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void integerMatching() {
 | 
			
		||||
        final PatternMatcher<? super Number, String> pm = PatternMatcher
 | 
			
		||||
                .when(Number.class::isInstance, x -> "Integer: " + x);
 | 
			
		||||
        assertEquals(pm.matches(42), Optional.of("Integer: 42"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void stringMatching() {
 | 
			
		||||
        final PatternMatcher<String, String> pm = PatternMatcher
 | 
			
		||||
                .when("world"::equals, x -> "Hello, " + x);
 | 
			
		||||
        assertThat(pm.matches("world"), is(Optional.of("Hello, world")));
 | 
			
		||||
        assertThat(pm.matches("Bob"), is(Optional.empty()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void emptyMatching() {
 | 
			
		||||
        final PatternMatcher<? super Number, Optional<String>> pm = PatternMatcher
 | 
			
		||||
                .when(Integer.class::isInstance, x -> Optional.empty());
 | 
			
		||||
        assertEquals(pm.matches(42), Optional.of(Optional.empty()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testOrWhen() {
 | 
			
		||||
        final PatternMatcher<Object, String> pm = PatternMatcher
 | 
			
		||||
                .when("world"::equals, x -> "Hello, " + x)
 | 
			
		||||
                .orWhen(Double.class::isInstance, x -> "Double: " + x);
 | 
			
		||||
        assertThat(pm.matches("world"), is(Optional.of("Hello, world")));
 | 
			
		||||
        assertThat(pm.matches(1.42), is(Optional.of("Double: 1.42")));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testOtherwise() {
 | 
			
		||||
        final PatternMatcher<Object, String> pm = PatternMatcher.when("world"::equals, x -> "Hello, " + x)
 | 
			
		||||
                .otherwise(x -> "got this object: " + x);
 | 
			
		||||
        assertThat(pm.matches("world"), is(Optional.of("Hello, world")));
 | 
			
		||||
        assertThat(pm.matches("foo"), is(Optional.of("got this object: foo")));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testCombine() {
 | 
			
		||||
        final PatternMatcher<Object, String> pm = PatternMatcher
 | 
			
		||||
                .when("world"::equals, x -> "Hello, " + x)
 | 
			
		||||
                .orWhen(Double.class::isInstance, x -> "Double: " + x)
 | 
			
		||||
                .otherwise(x -> "got this object: " + x);
 | 
			
		||||
        assertThat(pm.matches("world"), is(Optional.of("Hello, world")));
 | 
			
		||||
        assertThat(pm.matches(1.42), is(Optional.of("Double: 1.42")));
 | 
			
		||||
        assertThat(pm.matches("foo"), is(Optional.of("got this object: foo")));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user