Skip to content

Instantly share code, notes, and snippets.

@RayStarkMC
Last active November 28, 2020 00:36
Show Gist options
  • Save RayStarkMC/ddb4ffaacda7ecfe23ad94f55fe71a0b to your computer and use it in GitHub Desktop.
Save RayStarkMC/ddb4ffaacda7ecfe23ad94f55fe71a0b to your computer and use it in GitHub Desktop.
巨大な合成関数を生成した時のスタックオーバーフロー対策
package raystark.eflib.function.notnull.composer;
import org.jetbrains.annotations.NotNull;
import raystark.eflib.function.notnull.NF1;
import raystark.eflib.util.OneShotState;
import java.util.Deque;
import java.util.LinkedList;
public final class ComposerNF1<T1, R> {
private final Deque<NF1<?, ?>> functions;
private final OneShotState state;
private ComposerNF1(NF1<? super T1, ? extends R> init) {
var functions = new LinkedList<NF1<?, ?>>();
functions.add(init);
this.functions = functions;
this.state = OneShotState.newInstance();
}
private ComposerNF1(Deque<NF1<?, ?>> functions) {
this.functions = functions;
this.state = OneShotState.newInstance();
}
public <V> @NotNull ComposerNF1<T1, V> then(@NotNull NF1<? super R, ? extends V> after) {
return state.transitionThrowing(
() -> { functions.offerLast(after); return new ComposerNF1<>(functions); },
() -> new IllegalStateException("This composer has already been operated.")
);
}
public <V1> @NotNull ComposerNF1<V1, R> compose1(@NotNull NF1<? super V1, ? extends T1> before) {
return state.transitionThrowing(
() -> { functions.offerFirst(before); return new ComposerNF1<>(functions); },
() -> new IllegalStateException("This composer has already been operated.")
);
}
public @NotNull NF1<T1, R> compound() {
return state.transitionThrowing(
() -> new CompoundNF1<>(functions),
() -> new IllegalStateException("This composer has already been operated.")
);
}
public static <T1, R> @NotNull ComposerNF1<T1, R> of(@NotNull NF1<? super T1, ? extends R> nf1) {
return new ComposerNF1<>(nf1);
}
public static <T1, R, V>
@NotNull ComposerNF1<T1, V> concat(ComposerNF1<? super T1, ? extends R> left, ComposerNF1<? super R, ? extends V> right) {
var lefts = left.state.transitionThrowing(
() -> left.functions,
() -> new IllegalStateException("This composer has already been operated.")
);
var rights = right.state.transitionThrowing(
() -> right.functions,
() -> new IllegalStateException("This composer has already been operated.")
);
lefts.addAll(rights);
return new ComposerNF1<>(lefts);
}
}
package raystark.eflib.function.notnull.composer;
import org.jetbrains.annotations.NotNull;
import raystark.eflib.function.notnull.NF1;
import java.util.Deque;
import java.util.Iterator;
public class CompoundNF1<T1, R> implements NF1<T1, R> {
private final Deque<NF1<?, ?>> functions;
CompoundNF1(Deque<NF1<?, ?>> functions) {
this.functions = functions;
}
@Override
public @NotNull R apply(@NotNull T1 t1) {
Iterator<NF1<?, ?>> iterator = functions.iterator();
Object value = t1;
while(iterator.hasNext()) {
//functionsはT1 -> T2, T2 -> T3, ... , TN -> Rのように型が連続した関数のリストであるため安全
@SuppressWarnings("unchecked")
var function = (NF1<Object, Object>) iterator.next();
value = function.apply(value);
}
//functionsの最後の関数の型は必ずRであるので安全
@SuppressWarnings("unchecked")
var ret = (R) value;
return ret;
}
}
import raystark.eflib.function.notnull.NF1;
import raystark.eflib.function.notnull.NF2;
import raystark.eflib.function.notnull.composer.ComposerNF1;
import java.math.BigInteger;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Debug {
public static void main(String[] args) {
var foldLeftFrom50 = Collectors2.foldLeft(BigInteger.valueOf(50), BigInteger::subtract);
System.out.println(IntStream.rangeClosed(1, 10_000_000).mapToObj(BigInteger::valueOf).collect(foldLeftFrom50));
//System.out.println(IntStream.rangeClosed(1, 10_000_000).boxed().parallel().collect(Collectors2.foldLeft(50, (Integer a, Integer b) -> a - b)));
}
}
/*
* foldLeft/foldRight Collector
*
* Copyright(c) gakuzzzz
*
* This software is released under the MIT License.
* http://opensource.org/licenses/mit-license.php
*/
class Collectors2 {
public static <T1, T2> Collector<T1, ?, T2> foldRight(final T2 init, final NF2<? super T1, ? super T2, ? extends T2> nf2) {
return Collectors.mapping(
t1 -> nf2.apply(t1)::apply,
Collectors.collectingAndThen(
Collectors.<NF1<T2, T2>, ComposerNF1<T2, T2>>reducing(
ComposerNF1.of(NF1.identity()),
ComposerNF1::of,
ComposerNF1::concat
),
composer -> composer.compound().apply(init)
)
);
}
public static <T1, T2> Collector<T1, ?, T2> foldLeft(final T2 init, final NF2<? super T2, ? super T1, ? extends T2> nf2) {
return Collectors.mapping(
t1 -> nf2.swap2().apply(t1)::apply,
Collectors.collectingAndThen(
Collectors.<NF1<T2, T2>, ComposerNF1<T2, T2>>reducing(
ComposerNF1.of(NF1.identity()),
ComposerNF1::of,
ComposerNF1::concat
),
composer -> composer.compound().apply(init)
)
);
}
}
package raystark.eflib.util;
import org.jetbrains.annotations.NotNull;
import raystark.eflib.function.A;
import raystark.eflib.function.notnull.NS;
import raystark.eflib.option.Option;
import raystark.eflib.option.Option.None;
import raystark.eflib.option.Option.Some;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 一度限りの状態遷移をするオートマトンを表すインターフェース。
*/
public interface OneShotState {
default boolean isInit() {
return !isFinished();
}
boolean isFinished();
@NotNull
<R> Option<R> transition(@NotNull NS<? extends R> ifInit);
default void transition(@NotNull A ifInit) {
transition(() -> ifInit).orElse(() -> {}).run();
}
@NotNull
default <R> R transition(@NotNull NS<? extends R> ifInit, @NotNull NS<? extends R> ifFinished) {
return this.<R>transition(ifInit).orElse(ifFinished);
}
default void transition(@NotNull A ifInit, @NotNull A ifFinished) {
transition(() -> ifInit, () -> ifFinished).run();
}
default <R, X extends Exception>
@NotNull R transitionThrowing(@NotNull NS<? extends R> ifInit, @NotNull NS<? extends X> throwing) throws X {
return transition(ifInit).orElseThrow(throwing);
}
default <X extends Exception>
void transitionThrowing(@NotNull A ifInit, @NotNull NS<? extends X> throwing) throws X {
transition(() -> ifInit).orElseThrow(throwing).run();
}
static @NotNull OneShotState newInstance() {
return new OneShotState() {
private final AtomicBoolean isFinished = new AtomicBoolean();
@Override
public boolean isFinished() {
return isFinished.get();
}
@Override
public @NotNull <R> Option<R> transition(@NotNull NS<? extends R> ifInit) {
return checkFinished() ? None.of() : Some.of(ifInit.get());
}
private boolean checkFinished() {
return isFinished.getAndSet(true);
}
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment