2017-05-27 36 views
6

Tôi đang thử nghiệm với lambdas để giải trí. Tôi tạo ra một functor cho phép các thành phần của một lambda. Tuy nhiên, các phương tiện của chế phẩm chỉ cho phép chuyển đổi tuyến tính và không cho phép phân nhánh.Phân nhánh khi soạn lambdas từ các lambdas khác

Ý tưởng là tôi biết tôi sẽ có, trong tương lai, một cấu trúc dữ liệu trạng thái bất biến có hiệu quả. Tôi muốn soạn một biến đổi sẽ trích xuất một giá trị từ trạng thái; và sẽ thực hiện một loạt các bước, có thể hoặc không yêu cầu nhà nước, để thực hiện chuyển đổi.

Để kết thúc này, tôi tạo hai lớp. Giao diện chức năng hoạt động như java.util.function.Function, nhưng có một hàm sinh học theo phương pháp andThen, cho phép tham số trạng thái được truyền từ lambda sang 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)); 
    } 
} 

Các functor là khá đơn giản, có hai chức năng lập bản đồ (một trong đó sử dụng nhà nước, và một trong đó không), và hai chấm dứt phương pháp hoàn thiện biến đổi (một lần nữa, có và không có nhà nước).

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)); 
    } 
} 

Sơ lược (giả tạo) ví dụ:

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); 

Có ai có bất kỳ ý tưởng như thế nào tôi có thể thực hiện một phương pháp branch trên ProcedureContainer mà sẽ cho phép một chi nhánh có điều kiện trong việc thực hiện của người tiêu dùng chính thức . Tôi đang nghĩ đến một cái gì đó mà có thể làm việc ví dụ này:

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); 

tôi đã cố gắng bằng cách tạo ra một mới BranchProcedureContainer, trong đó có một phương pháp mapterminateBranch. Vấn đề này là tôi không biết làm thế nào để hợp nhất hai nhánh theo cách mà chỉ có nhánh mới được chạy.

Không có hạn chế về việc tạo lớp mới hoặc thêm phương thức vào lớp hiện có.

+1

tiếc là bạn không nhận được bất kỳ sự chú ý nào ở đây. Tôi cũng rất thích điều này. Ít nhất tôi có thể làm là bỏ phiếu. – Eugene

Trả lời

1

Tôi đã có thể đặt một giải pháp với nhau. Nhưng, tôi không thấy nó đặc biệt tao nhã. Vì vậy, vui lòng gửi các giải pháp khác (hoặc phiên bản trực quan hơn của giải pháp này).

Ban đầu, tôi đã thử tạo vùng chứa trạng thái chứa một boolean cho biết một chi nhánh cụ thể có được sử dụng hay không. Đây không phải là một tình trạng không đi vì nhà nước không được thông qua một cách chính xác. Vì vậy, thay vào đó tôi tạo ra một container giá trị:

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 
} 

sau đó tôi viết lại giao diện chức năng để tận dụng các container mới:

@FunctionalInterface 
public interface Procedure<S, T> { 

    ValueContainer<T> procede(S stateStructure); 
} 

Với việc bổ sung các ValueContainer, tôi không muốn người dùng phải bỏ giá trị cho mỗi phương thức. Do đó, phương thức mặc định đã được mở rộng để tính đến vùng chứa. Ngoài ra, logic đã được thêm vào để đối phó với các trường hợp mà thủ tục là một phần của một chi nhánh không sử dụng/chấm dứt.

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); 
    }; 
} 

Từ đó, tôi chỉ phải mở rộng ProcedureContainer để có một phương pháp branch; và đã làm lại phương pháp chấm dứt để tính đến số ValueContainer và trường hợp chi nhánh đã kết thúc.(Mã bên dưới để lại các phương thức quá tải)

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()); 
     }; 
    } 
} 

Phương thức chi nhánh trả về một chi nhánh mới, BranchProcedureContainer. Lớp này có phương thức endBranch liên kết lại thành phiên bản mới ProcedureContainer. (Một lần nữa, phương pháp quá tải đang rời đi)

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); 
    } 
} 

Một vấn đề tôi thấy với phương pháp này (mặc dù tôi chắc chắn rằng có rất nhiều) là các cuộc gọi lặp đi lặp lại để xác định xem một chi nhánh được chấm dứt hay không. Sẽ tốt hơn nếu kiểm tra đó chỉ được thực hiện tại điểm phân nhánh.

Mã đầy đủ có thể được tìm thấy my github page.

LƯU Ý: Tôi hiểu rằng tôi đã sử dụng 'chấm dứt' cho cả hai chỉ định khi một chi nhánh được hoàn thành và khi chi nhánh không bao giờ chạy. Tôi vẫn đang cố nghĩ về một quy ước đặt tên tốt hơn. Mở để đề xuất.

+0

có lẽ đây là một đơn nguyên. bạn có được coi là tùy chọn không? –

+0

@RayTayek, cảm ơn lời khuyên. Tôi đã thử sử dụng một tùy chọn, nhưng chạy vào một vài thách thức. Cuối cùng, ValueContainer đã cho tôi tự do hơn. – JRogerC