2017-05-27 52 views
6

我正在嘗試使用lambda發現趣味。我創建了一個函數,它允許lambda的組成。但是,構圖的手段只允許線性轉換,並且不允許分支。從其他lambda發佈lambdas時分支

這個想法是,我知道我將在未來有一個有效的不可變狀態數據結構。我想編寫一個將從狀態中提取值的轉換;並將執行一系列可能或可能不需要該狀態的步驟來執行變換。

爲此,我創建了兩個類。功能接口的工作方式與java.util.function.Function類似,但在andThen方法中使用BiFunction,該方法允許將狀態參數從lambda傳遞到lambda。

import java.util.Objects; 
import java.util.function.BiFunction; 

@FunctionalInterface 
public interface Procedure<S, T> { 

    T procede(S stateStructure); 

    default <R> Procedure<S, R> andThen(BiFunction<S, T, R> after) { 
     Objects.requireNonNull(after); 
     return (param) -> after.apply(param, procede(param)); 
    } 
} 

函子是相當簡單的,具有兩個映射函數(一個其利用狀態,和一個其中沒有),以及兩個終止該完成的轉化(再次,具有和不具有狀態)的方法。

import java.util.function.BiConsumer; 
import java.util.function.BiFunction; 
import java.util.function.Consumer; 
import java.util.function.Function; 

public class ProcedureContainer<S, T> { 

    protected final Procedure<S, T> procedure; 

    protected ProcedureContainer(final Procedure<S, T> procedure) { 
     this.procedure = procedure; 
    } 

    public static <S, R> ProcedureContainer<S, R> initializeContainer(
      final Function<S, R> initialDataRetriever) { 

     return new ProcedureContainer<>(initialDataRetriever::apply); 
    } 

    public <R> ProcedureContainer<S, R> map(final BiFunction<S, T, R> mapper) { 
     return new ProcedureContainer<>(procedure.andThen(mapper)); 
    } 

    public <R> ProcedureContainer<S, R> map(final Function<T, R> mapper) { 
     BiFunction<S, T, R> subMapper = 
       (ignored, stagedData) -> mapper.apply(stagedData); 
     return new ProcedureContainer<>(procedure.andThen(subMapper)); 
    } 

    public Consumer<S> terminate(final BiConsumer<S, T> consumer) { 
     return (param) -> consumer.accept(param, procedure.procede(param)); 
    } 

    public Consumer<S> terminate(final Consumer<T> consumer) { 
     return (param) -> consumer.accept(procedure.procede(param)); 
    } 
} 

一個簡短的(人爲)例如:

StateStruct state = new StateStruct(); 
state.setJson("{\"data\":\"meow, meow, I'm a cow\"}"); 
state.setRequestedField("data"); 

Consumer<StateStruct> consumer = ProcedureContainer 
    .initializeContainer(SateStruct::getJson) 
    .map(JSONObject::new) 
    .map((state, jsonObj) -> jsonObject.getString(state.getRequsetedField())) 
    .terminate(System.out::singLoudly); 

consumer.accept(state); 

沒有人有我怎麼能實現對ProcedureContainer一個branch方法的任何想法,將允許最終消費者的執行條件分支。我想的東西,這將使這個例子的工作:

StateStruct state = new StateStruct(); 
state.setJson("{\"data\":\"meow, meow, I'm a cow\"}"); 
state.setRequestedField("data"); 
state.setDefaultMessage("There is no data... only sheep"); 

Consumer<StateStruct> consumer = ProcedureContainer 
    .initializeContainer(SateStruct::getJson) 
    .map(JSONObject::new) 

    .branch((state, jsonObj) -> !jsonObject.getString(state.getRequsetedField())) 
    .terminateBranch((state, json) -> System.out.lament(state.getDefaultMessage())) 

    .map((state, jsonObj) -> jsonObject.getString(state.getRequsetedField())) 
    .terminate(System.out::singLoudly); 

consumer.accept(state); 

我已經通過創建一個新BranchProcedureContainer,其中有一個mapterminateBranch方法嘗試。這個問題是,我不知道如何合併這兩個分支的方式,只有分支得到運行。

創建新類或向現有類添加方法沒有限制。

+1

這樣的遺憾你在這裏沒有得到任何關注。我對此也很感興趣。至少我能做的是投票。 – Eugene

回答

1

我能夠把解決方案放在一起。但是,我不覺得它特別優雅。所以,請隨時提交其他解決方案(或更具創新性的解決方案)。

我最初嘗試創建一個狀態容器,其中包含一個布爾值,說明是否使用了特定的分支。這是不行的,因爲國家沒有正確地傳遞。所以,相反我創建了一個價值容器:

class ValueContainer<T> { 

    private final T value; 
    private final Boolean terminated; 

    private ValueContainer(final T value, final Boolean terminated) { 
     this.value = value; 
     this.terminated = terminated; 
    } 

    public static <T> ValueContainer<T> of(final T value) { 
     return new ValueContainer<>(value, false); 
    } 

    public static <T> ValueContainer<T> terminated() { 
     return new ValueContainer<>((T) null, true); 
    } 

    //...getters 
} 

然後我重寫了功能接口,以利用新的容器:

@FunctionalInterface 
public interface Procedure<S, T> { 

    ValueContainer<T> procede(S stateStructure); 
} 

由於增加了ValueContainer的,我不想讓用戶可以打開每種方法的值。因此,默認方法已擴展到佔用容器。另外,邏輯被添加來處理過程是未使用/終止分支的一部分的情況。

default <R> Procedure<S, R> andThen(BiFunction<S, T, R> after) { 
    Objects.requireNonNull(after); 
    return (param) -> { 
     ValueContainer<T> intermediateValue = procede(param); 
     if (intermediateValue.isTerminated()) 
      return ValueContainer.<R>terminated(); 
     R returnValue = after.apply(param, intermediateValue.getValue()); 
     return ValueContainer.of(returnValue); 
    }; 
} 

從那裏,我不得不擴大ProcedureContainerbranch方法;並重新編制了終止方法以解釋ValueContainer和終止分支案例。(下面的代碼離開關重載方法)

public class ProcedureContainer<S, T> { 

    protected final Procedure<S, T> procedure; 

    protected ProcedureContainer(final Procedure<S, T> procedure) { 
     this.procedure = procedure; 
    } 

    public static <S, R> ProcedureContainer<S, R> initializeContainer(
      final Function<S, R> initialDataRetriever) { 

     Procedure<S, R> initializer = (paramContainer) -> { 
      R initialValue = initialDataRetriever.apply(paramContainer); 
      return ValueContainer.of(initialValue); 
     }; 
     return new ProcedureContainer<>(initializer); 
    } 

    public <R> ProcedureContainer<S, R> map(final BiFunction<S, T, R> mapper) { 
     return new ProcedureContainer<>(procedure.andThen(mapper)); 
    } 

    public BranchProcedureContainer<S, T, T> branch(final BiPredicate<S, T> predicate) { 

     return BranchProcedureContainer.branch(procedure, predicate); 
    } 

    public Consumer<S> terminate(final BiConsumer<S, T> consumer) { 
     return (param) -> { 
      ValueContainer<T> finalValue = procedure.procede(param); 
      if (finalValue.isTerminated()) 
       return; 

      consumer.accept(param, finalValue.getValue()); 
     }; 
    } 
} 

分支方法返回一個新的類,BranchProcedureContainer,它處理建立分支。該課程有一個endBranch方法,該方法與ProcedureContainer的新實例相關聯。 (同樣,重載方法都不放過)

public class BranchProcedureContainer<S, T, R> { 

    private final Procedure<S, T> baseProcedure; 
    private final BiPredicate<S, T> predicate; 
    private final BiFunction<S, ValueContainer<T>, ValueContainer<R>> branchProcedure; 

    private BranchProcedureContainer(
      final Procedure<S, T> baseProcedure, 
      final BiPredicate<S, T> predicate, 
      final BiFunction<S, ValueContainer<T>, ValueContainer<R>> branchProcedure) { 

     this.baseProcedure = baseProcedure; 
     this.predicate = predicate; 
     this.branchProcedure = branchProcedure; 
    } 

    protected static <S, T> BranchProcedureContainer<S, T, T> branch(
      final Procedure<S, T> baseProcedure, 
      final BiPredicate<S, T> predicate) { 

     return new BranchProcedureContainer<>(baseProcedure, predicate, (s, v) -> v); 
    } 

    public <RR> BranchProcedureContainer<S, T, RR> map(
      final BiFunction<S, R, RR> mapper) { 

     BiFunction<S, ValueContainer<T>, ValueContainer<RR>> fullMapper = (s, vT) -> { 
      if (vT.isTerminated()) 
       return ValueContainer.<RR>terminated(); 

      ValueContainer<R> intermediateValue = branchProcedure.apply(s, vT); 
      if (intermediateValue.isTerminated()) 
       return ValueContainer.<RR>terminated(); 

      RR finalValue = mapper.apply(s, intermediateValue.getValue()); 
      return ValueContainer.of(finalValue); 
     }; 
     return new BranchProcedureContainer<>(baseProcedure, predicate, fullMapper); 
    } 

    public ProcedureContainer<S, T> endBranch(final BiConsumer<S, R> consumer) { 

     Procedure<S, T> mergedBranch = (state) -> { 
      ValueContainer<T> startingPoint = baseProcedure.procede(state); 
      if (startingPoint.isTerminated()) 
       return ValueContainer.<T>terminated(); 

      if (!predicate.test(state, startingPoint.getValue())) 
       return startingPoint; 

      ValueContainer<R> intermediateValue = branchProcedure.apply(state, startingPoint); 
      if (intermediateValue.isTerminated()) 
       return ValueContainer.<T>terminated(); 
      consumer.accept(state, intermediateValue.getValue()); 
      return ValueContainer.<T>terminated(); 
     }; 

     return new ProcedureContainer<>(mergedBranch); 
    } 
} 

的一個問題,我用這個方法看(雖然我敢肯定有很多)是重複調用,以確定分支是否被終止或不。如果該檢查只在分支點完成,那將會很好。

完整的代碼可以找到my github page

注意:我知道我已經使用'terminate'來指定分支完成時以及分支從未運行的時間。我仍然試圖想到一個更好的命名約定。接受建議。

+0

也許這是一個單子。你認爲可選嗎? –

+0

@RayTayek,謝謝你的建議。我曾嘗試使用可選方案,但遇到了一些挑戰。最後,ValueContainer給了我更多的自由。 – JRogerC