2015-07-27 15 views
9

Tôi đang cố gắng chuyển đổi vòng lặp for thành mã chức năng. Tôi cần phải nhìn về phía trước một giá trị và cũng nhìn phía sau một giá trị. Có thể sử dụng luồng không? Mã sau đây là chuyển đổi văn bản La Mã thành giá trị số. Không chắc chắn nếu giảm phương pháp với hai/ba đối số có thể giúp đỡ ở đây.Có thể nhận phần tử tiếp theo trong Luồng không?

int previousCharValue = 0; 
int total = 0; 

for (int i = 0; i < input.length(); i++) { 
    char current = input.charAt(i); 

    RomanNumeral romanNum = RomanNumeral.valueOf(Character.toString(current)); 

    if (previousCharValue > 0) { 
     total += (romanNum.getNumericValue() - previousCharValue); 
     previousCharValue = 0; 
    } else { 
     if (i < input.length() - 1) { 

      char next = input.charAt(i + 1); 
      RomanNumeral nextNum = RomanNumeral.valueOf(Character.toString(next)); 
      if (romanNum.getNumericValue() < nextNum.getNumericValue()) { 
       previousCharValue = romanNum.getNumericValue(); 
      } 
     } 
     if (previousCharValue == 0) { 
      total += romanNum.getNumericValue(); 
     } 

    } 

} 
+0

Tôi nghĩ rằng nó không phải là có thể, câu hỏi thú vị anyway. –

+3

@sidgate 'Chuỗi [] ký tự = input.split (" "); IntStream.range (0, characters.length) ... 'một cái gì đó trên dòng này nhưng không phải trên một máy java-8 để thử điều này. – CKing

+0

Đó thực sự là mã xoắn. Rất khó để theo dõi và tôi vẫn không chắc liệu nó có đúng không. Tôi nghĩ rằng, bạn nên cố gắng viết một giải pháp thích hợp mà không cần sao chép mã với một vòng lặp bình thường đầu tiên. – Holger

Trả lời

9

Không, điều này không thể sử dụng suối, ít nhất là không dễ dàng. API luồng tóm tắt từ thứ tự các thành phần được xử lý: luồng có thể được xử lý song song hoặc theo thứ tự ngược lại. Vì vậy, "phần tử tiếp theo" và "phần tử trước" không tồn tại trong luồng trừu tượng.

Bạn nên sử dụng các API phù hợp nhất cho công việc: dòng là tuyệt vời nếu bạn cần phải áp dụng một số hoạt động để tất cả yếu tố của một bộ sưu tập và bạn không quan tâm đến thứ tự. Nếu bạn cần xử lý các phần tử theo một thứ tự nhất định, bạn phải sử dụng các trình vòng lặp hoặc có thể truy cập vào các phần tử danh sách thông qua các chỉ mục.

2

Tôi chưa thấy trường hợp sử dụng này với luồng, vì vậy tôi không thể nói nếu có thể hay không. Nhưng khi tôi cần sử dụng các luồng có chỉ mục, tôi chọn IntStream#range(0, table.length) và sau đó trong lambdas tôi nhận được giá trị từ bảng/danh sách này.

Ví dụ

int[] arr = {1,2,3,4}; 
    int result = IntStream.range(0, arr.length) 
      .map(idx->idx>0 ? arr[idx] + arr[idx-1]:arr[idx]) 
      .sum(); 
2

Do tính chất của luồng, bạn không biết phần tử tiếp theo trừ khi bạn đọc nó. Do đó, việc lấy trực tiếp phần tử tiếp theo là không thể khi xử lý phần tử hiện tại. Tuy nhiên kể từ khi bạn đang đọc hiện yếu tố bạn obiously biết những gì đã được đọc trước, vì vậy để đạt được mục tiêu như "accesing yếu tố trước" và "truy cập phần tử tiếp theo", bạn có thể dựa trên lịch sử của các yếu tố đó đều đã xử lý.

Sau hai giải pháp là có thể cho vấn đề của bạn:

  1. Nhận quyền truy cập vào đọc trước đây yếu tố. Bằng cách này bạn biết các yếu tố hiện và số xác định trước đó đọc yếu tố
  2. Giả sử rằng tại thời điểm xử lý dòng bạn đọc tiếp theo yếu tố và nguyên tố hiện được đọc trong lần lặp trước. Nói cách khác, bạn xem trước phần tử đã đọc là phần tử "hiện tại" và hiện đang được xử lý là tiếp theo (xem bên dưới).

Giải pháp 1 - implemenation

Đầu tiên chúng ta cần một cấu trúc dữ liệu mà sẽ cho phép theo dõi các dữ liệu chảy qua con suối. Lựa chọn tốt có thể là một thể hiện của Queue bởi vì hàng đợi theo bản chất của chúng cho phép dữ liệu chảy qua chúng. Chúng tôi chỉ cần ràng buộc hàng đợi với số phần tử cuối cùng mà chúng tôi muốn biết (có thể là 3 phần tử cho trường hợp sử dụng của bạn).Đối với điều này, chúng tôi tạo hàng đợi "bị chặn" giữ lịch sử như sau:

public class StreamHistory<T> { 

    private final int numberOfElementsToRemember; 
    private LinkedList<T> queue = new LinkedList<T>(); // queue will store at most numberOfElementsToRemember 

    public StreamHistory(int numberOfElementsToRemember) { 
     this.numberOfElementsToRemember = numberOfElementsToRemember; 
    } 

    public StreamHistory save(T curElem) { 

     if (queue.size() == numberOfElementsToRemember) { 
      queue.pollLast(); // remove last to keep only requested number of elements 
     } 

     queue.offerFirst(curElem); 

     return this; 
    } 


    public LinkedList<T> getLastElements() { 
     return queue; // or return immutable copy or immutable view on the queue. Depends on what you want. 
    } 
} 

Thông số chung T là loại yếu tố thực tế của luồng. Phương pháp lưu trả về tham chiếu đến thể hiện của StreamHistory hiện tại để tích hợp tốt hơn với java Stream api (xem bên dưới) và nó không thực sự bắt buộc. Bây giờ điều duy nhất cần làm là chuyển đổi luồng các phần tử thành luồng của các phiên bản StreamHistory (trong đó mỗi phần tử tiếp theo của luồng sẽ giữ n các đối tượng thực sự mới nhất đi qua luồng).

public class StreamHistoryTest { 
    public static void main(String[] args) { 
    Stream<Character> charactersStream = IntStream.range(97, 123).mapToObj(code -> (char) code); // original stream 

    StreamHistory<Character> streamHistory = new StreamHistory<>(3); // instance of StreamHistory which will store last 3 elements 

    charactersStream.map(character -> streamHistory.save(character)).forEach(history -> { 
     history.getLastElements().forEach(System.out::print); 
     System.out.println(); 
    }); 

    } 

} 

Trong ví dụ trên, trước tiên chúng ta tạo một luồng tất cả các chữ cái trong bảng chữ cái. Hơn chúng ta tạo ra thể hiện của StreamHistory mà sẽ được đẩy tới mỗi lần lặp của ánh xạ() gọi trên luồng ban đầu. Qua gọi tới map(), chúng ta chuyển đổi sang dòng có chứa các tham chiếu đến cá thể StreamHistory của chúng ta.

Lưu ý rằng mỗi khi dữ liệu truyền qua luồng gốc, lệnh gọi tới streamHistory.save (ký tự) sẽ cập nhật nội dung của đối tượng streamHistory để phản ánh trạng thái hiện tại của luồng.

Cuối cùng trong mỗi lần lặp, chúng tôi in 3 ký tự đã lưu cuối cùng. Kết quả của phương pháp này là như sau:

a 
ba 
cba 
dcb 
edc 
fed 
gfe 
hgf 
ihg 
jih 
kji 
lkj 
mlk 
nml 
onm 
pon 
qpo 
rqp 
srq 
tsr 
uts 
vut 
wvu 
xwv 
yxw 
zyx 

Giải pháp 2 - thực hiện

Trong khi giải pháp 1 sẽ ở hầu hết các trường hợp thực hiện công việc và là khá dễ dàng để làm theo, có những trường hợp sử dụng là khả năng kiểm tra yếu tố tiếp theo và trước đó thực sự thuận tiện. Trong kịch bản như vậy, chúng ta chỉ quan tâm đến ba phần tử (pevious, current, next) và chỉ có một phần tử không quan trọng (ví dụ đơn giản xem xét câu đố sau: "cho một luồng các số trả về một số ba số tiếp theo số tiền cao nhất "). Để giải quyết các trường hợp sử dụng như vậy, chúng ta có thể muốn có api thuận tiện hơn so với lớp StreamHistory.

Đối với kịch bản này, chúng tôi giới thiệu một biến thể mới của lớp StreamHistory (mà chúng tôi gọi là StreamNeighbours). Lớp học sẽ cho phép kiểm tra trực tiếp trước và trực tiếp phần tử tiếp theo. Việc xử lý sẽ được thực hiện đúng lúc "T-1" (nghĩa là: phần tử gốc được xử lý hiện tại được xem là phần tử tiếp theo và phần tử gốc được xử lý trước đây được coi là phần tử hiện tại). Bằng cách này, chúng tôi, theo một nghĩa nào đó, kiểm tra một yếu tố phía trước.

Lớp sửa đổi được như sau:

public class StreamNeighbours<T> { 
    private LinkedList<T> queue = new LinkedList(); // queue will store one element before current and one after 
    private boolean threeElementsRead; // at least three items were added - only if we have three items we can inspect "next" and "previous" element 

    /** 
    * Allows to handle situation when only one element was read, so technically this instance of StreamNeighbours is not 
    * yet ready to return next element 
    */ 
    public boolean isFirst() { 
     return queue.size() == 1; 
    } 

    /** 
    * Allows to read first element in case less than tree elements were read, so technically this instance of StreamNeighbours is 
    * not yet ready to return both next and previous element 
    * @return 
    */ 
    public T getFirst() { 
     if (isFirst()) { 
      return queue.getFirst(); 
     } else if (isSecond()) { 
      return queue.get(1); 
     } else { 
      throw new IllegalStateException("Call to getFirst() only possible when one or two elements were added. Call to getCurrent() instead. To inspect the number of elements call to isFirst() or isSecond()."); 
     } 
    } 

    /** 
    * Allows to handle situation when only two element were read, so technically this instance of StreamNeighbours is not 
    * yet ready to return next element (because we always need 3 elements to have previos and next element) 
    */ 
    public boolean isSecond() { 
     return queue.size() == 2; 
    } 

    public T getSecond() { 
     if (!isSecond()) { 
      throw new IllegalStateException("Call to getSecond() only possible when one two elements were added. Call to getFirst() or getCurrent() instead."); 
     } 
     return queue.getFirst(); 
    } 


    /** 
    * Allows to check that this instance of StreamNeighbours is ready to return both next and previous element. 
    * @return 
    */ 
    public boolean areThreeElementsRead() { 
     return threeElementsRead; 
    } 


    public StreamNeighbours<T> addNext(T nextElem) { 

     if (queue.size() == 3) { 
      queue.pollLast(); // remove last to keep only three 
     } 

     queue.offerFirst(nextElem); 

     if (!areThreeElementsRead() && queue.size() == 3) { 
      threeElementsRead = true; 
     } 

     return this; 
    } 


    public T getCurrent() { 
     ensureReadyForReading(); 
     return queue.get(1); // current element is always in the middle when three elements were read 

    } 

    public T getPrevious() { 
     if (!isFirst()) { 
      return queue.getLast(); 
     } else { 
      throw new IllegalStateException("Unable to read previous element of first element. Call to isFirst() to know if it first element or not."); 
     } 
    } 

    public T getNext() { 
     ensureReadyForReading(); 
     return queue.getFirst(); 
    } 

    private void ensureReadyForReading() { 
     if (!areThreeElementsRead()) { 
      throw new IllegalStateException("Queue is not threeElementsRead for reading (less than two elements were added). Call to areThreeElementsRead() to know if it's ok to call to getCurrent()"); 
     } 
    } 

} 

Bây giờ, giả định rằng ba yếu tố đã được đọc, chúng ta có thể truy cập trực tiếp hiện yếu tố (được phần tử đi qua suối lúc T-1) , chúng ta có thể truy cập vào tiếp theo yếu tố (được nguyên tố này sẽ vào lúc này thông qua con suối) và trước (đó là các yếu tố đi qua suối lúc T-2):

public class StreamTest { 
    public static void main(String[] args) { 
    Stream<Character> charactersStream = IntStream.range(97, 123).mapToObj(code -> (char) code); 

    StreamNeighbours<Character> streamNeighbours = new StreamNeighbours<Character>(); 


    charactersStream.map(character -> streamNeighbours.addNext(character)).forEach(neighbours -> { 
     // NOTE: if you want to have access the values before instance of StreamNeighbours is ready to serve three elements 
     // you can use belows methods like isFirst() -> getFirst(), isSecond() -> getSecond() 
     // 
     //   if (curNeighbours.isFirst()) { 
     //    Character currentChar = curNeighbours.getFirst(); 
     //    System.out.println("???" + " " + currentChar + " " + "???"); 
     //   } else if (curNeighbours.isSecond()) { 
     //    Character currentChar = curNeighbours.getSecond(); 
     //    System.out.println(String.valueOf(curNeighbours.getFirst()) + " " + currentChar + " " + "???"); 
     // 
     //   } 
     // 
     // OTHERWISE: you are only interested in tupples consisting of three elements, so three elements needed to be read 

     if (neighbours.areThreeElementsRead()) { 
     System.out.println(neighbours.getPrevious() + " " + neighbours.getCurrent() + " " + neighbours.getNext()); 
     } 
    }); 

    } 

} 

Kết quả của việc này là như sau:

a b c 
b c d 
c d e 
d e f 
e f g 
f g h 
g h i 
h i j 
i j k 
j k l 
k l m 
l m n 
m n o 
n o p 
o p q 
p q r 
q r s 
r s t 
s t u 
t u v 
u v w 
v w x 
w x y 
x y z 

By lớp StreamNeighbours nó dễ dàng hơn để theo dõi các yếu tố trước/sau (vì chúng tôi có phương pháp với những cái tên thích hợp), trong khi ở lớp StreamHistory này là cồng kềnh hơn vì chúng ta cần phải tự "đảo ngược" thứ tự của hàng đợi để đạt được điều này.

Các vấn đề liên quan