2015-08-13 14 views
5

Tôi đã tự hỏi liệu có thể sử dụng .collect() trên một trình lặp để lấy các mục tại một chỉ mục cụ thể hay không. Ví dụ nếu tôi bắt đầu với một chuỗi, tôi sẽ thường làm:Thu thập các mục từ một trình lặp tại một chỉ mục cụ thể

let line = "Some line of text for example"; 
let l = line.split(" "); 
let lvec: Vec<&str> = l.collect(); 
let text = &lvec[3]; 

Nhưng những gì sẽ là tốt đẹp là một cái gì đó như:

let text: &str = l.collect(index=(3)); 

Trả lời

7

Không, không phải; tuy nhiên, bạn có thể dễ dàng lọc trước khi bạn thu thập, trong thực tế có được hiệu quả tương tự.

Nếu bạn muốn lọc theo chỉ mục, bạn cần phải thêm chỉ mục trong và sau đó dải nó sau đó:

  • enumerate (để thêm các chỉ số để các phần tử)
  • filter dựa trên chỉ số này
  • map dải chỉ số từ các yếu tố

Hoặc trong mã:

01.
fn main() { 
    let line = "Some line of text for example"; 
    let l = line.split(" ") 
       .enumerate() 
       .filter(|&(i, _)| i == 3) 
       .map(|(_, e)| e); 
    let lvec: Vec<&str> = l.collect(); 
    let text = &lvec[0]; 
    println!("{}", text); 
} 

Nếu bạn chỉ muốn có một chỉ mục duy nhất (và do đó phần tử), thì việc sử dụng nth sẽ dễ dàng hơn nhiều. Nó trả về một Option<&str> đây, mà bạn cần phải chăm sóc:

fn main() { 
    let line = "Some line of text for example"; 
    let text = line.split(" ").nth(3).unwrap(); 
    println!("{}", text); 
} 

Nếu bạn có thể có một vị tùy ý nhưng muốn chỉ là yếu tố đầu tiên phù hợp, sau đó thu thập vào một Vec là không hiệu quả: nó sẽ tiêu thụ toàn bộ iterator (không lười biếng) và phân bổ tiềm năng rất nhiều bộ nhớ mà không cần thiết ở tất cả.

Bạn đang như vậy, tốt hơn hết chỉ đơn giản yêu cầu các yếu tố đầu tiên sử dụng phương pháp next của iterator, mà trả về một Option<&str> đây:

fn main() { 
    let line = "Some line of text for example"; 
    let text = line.split(" ") 
        .enumerate() 
        .filter(|&(i, _)| i % 7 == 3) 
        .map(|(_, e)| e) 
        .next() 
        .unwrap(); 
    println!("{}", text); 
} 

Nếu bạn muốn chọn phần của kết quả, bởi chỉ số , bạn cũng có thể sử dụng skiptake trước khi thu thập, nhưng tôi đoán bạn có đủ lựa chọn thay thế được trình bày ở đây rồi.

+0

Cảm ơn bạn, rất nhiều để có ở đây nhưng rất hữu ích. Bạn đã đề cập rằng việc thu thập vào một Vec là không hiệu quả. Bạn có thể nhận xét về tốc độ thực hiện line.split.enumerate.filter.map.unwrap nhiều lần để lấy các mục khác nhau từ chuỗi so với thu thập toàn bộ đồ vật vào một Vec và sau đó sử dụng chỉ mục vector để truy xuất các mục. Ví dụ như một quy tắc nhỏ, nếu cần truy cập 1/4 mục, tôi sẽ làm phương pháp nào? – kezzos

+0

@kezzos: Tôi đã đề cập rằng việc thu thập * mọi thứ * vào một véc tơ không hiệu quả, và tôi cũng đưa ra lý do: lặp lại cho đến khi bạn không quan tâm đến những phần cuối cùng và phân bổ bộ nhớ. Nếu bạn cần phải thu thập 1/4 của các mảnh, sau đó tôi sẽ không lo lắng về việc cấp phát bộ nhớ, tuy nhiên bạn vẫn có thể đạt được từ cắt ngắn lặp (sử dụng 'take'). –

+2

Ngoài ra còn có ['filter_map'] (http://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map) để kết hợp các bước' filter' và 'map'. – Shepmaster

6

Có một chức năng nth trên Iterator mà thực hiện điều này:

let text = line.split(" ").nth(3).unwrap(); 
+0

Cảm ơn, điều này cũng trả về tùy chọn <&str>? – kezzos

+0

Chắc chắn, đó là những gì 'unwrap' là dành cho. Về cơ bản nó là 'fn unwrap (x: Option ) {nếu cho phép Some (v) = x {v} else {panic! (" Meh ")}}'. – filmor

2

Không; bạn có thể sử dụng takenext, mặc dù:

let line = "Some line of text for example"; 
let l = line.split(" "); 
let text = l.skip(3).next(); 

Lưu ý rằng kết quả này trong text là một Option<&str>, như không có đảm bảo rằng trình tự thực ít nhất bốn yếu tố.

Phụ Lục: sử dụng nth chắc chắn là ngắn hơn, mặc dù tôi thích được rõ ràng về thực tế là truy cập vào thứ yếu tố ncủa một iterator thiết tiêu thụ tất cả các yếu tố trước khi nó.

+0

Về phụ lục của bạn, tôi thấy quan điểm của bạn. Tuy nhiên, gỉ là khá rõ ràng dù sao đi nữa: Nếu bạn viết nó trong một dòng duy nhất nó là tốt dù sao, bởi vì bạn sẽ không sử dụng (một phần tiêu thụ) chia bất cứ nơi nào khác. Nếu bạn muốn chia tách và lấy mục thứ 4, trình biên dịch sẽ buộc bạn phải viết 'let mut l = line.split ("") 'để có thể sử dụng' nnth'. – filmor

0

Đối với bất kỳ ai quan tâm, bạn có thể tải nhiều thứ thú vị với trình vòng lặp (nhờ Matthieu M), ví dụ để nhận nhiều từ 'từ một chuỗi theo chỉ mục của chúng, bạn có thể sử dụng filter hoặc || để kiểm tra nhiều chỉ mục!

let line = "FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062" 
let words: Vec<&str> = line.split(" ") 
          .enumerate() 
          .filter(|&(i, _)| i==1 || i==3 || i==6) 
          .map(|(_, e) | e) 
          .collect(); 
Các vấn đề liên quan