2017-10-26 9 views
0

タプルのVecのフィールドを含むEnclosingObjectという構造体があります。空白で区切られた文字列を異なるタイプのタプルのVecに慣用的に解析する

これは私がこれまでの(無効の場合は無視して出ているものです... <number of tuples> <tuple1 str1> <tuple1 str2> <tuple1 i32> <tuple2 str1> <tuple2 str2>:私はEnclosingObjectは、以下の構造を持つ文字列から解析することが可能な方法で、この構造体のFromStrを実装したいですタプルの数):

use std::str::FromStr; 
use std::num::ParseIntError; 

#[derive(Debug)] 
struct EnclosingObject{ 
    tuples: Vec<(String, String, i32)>, 
} 

impl FromStr for EnclosingObject { 
    type Err = ParseIntError; 

    fn from_str(s: &str) -> Result<Self, Self::Err> { 
     let elems_vec = s.split_whitespace().collect::<Vec<_>>(); 
     let mut elems = elems_vec.as_slice(); 

     let num_tuples = elems[0].parse::<usize>()?; 
     elems = &elems[1..]; 
     let mut tuples = Vec::with_capacity(num_tuples); 
     for chunk in elems.chunks(3).take(num_tuples){ 
      tuples.push((chunk[0].into(), 
        chunk[1].into(), 
        chunk[2].parse::<i32>()?)); 
     } 

     Ok(EnclosingObject{ 
      tuples : tuples 
     }) 
    } 
} 

fn main(){ 
    println!("{:?}", EnclosingObject::from_str("3 a b 42 c d 32 e f 50")); 
} 

playground

それはプリントアウトし、有効な文字列のため、予想通り:

Ok(EnclosingObject { tuples: [("a", "b", 42), ("c", "d", 32), ("e", "f", 50)] }) 

などの無効な文字列の場合。 "50 F 3 BのXはD = 32 E":

Err(ParseIntError { kind: InvalidDigit }) 

私はこのようなイテレータを使用するなど、より洗練/慣用の方法でタプルのこのVecを解析することはできますか?

私はmapcollectの組み合わせを試してみましたが、これに伴う問題は、エラー処理です:

let tuples = elems 
      .chunks(3) 
      .take(num_tuples) 
      .map(|chunk| (chunk[0].into(), 
        chunk[1].into(), 
        chunk[2].parse::<i32>()?)) 
      .collect(); 

questionmark-オペレータは(タプル内の)このような状況では動作しないようです。だから私はそれをちょっと変えました:

let tuples = try!(elems 
      .chunks(3) 
      .take(num_tuples) 
      .map(|chunk| { 
        let integer = chunk[2].parse::<i32>()?; 
        Ok((chunk[0].into(), 
        chunk[1].into(), 
        integer))}) 
      .collect()); 

...もう少し面倒です。

+3

作業コードがあることを考慮すると、この質問は[Code Review SE](// codereview.stackexchange.com)に適しているかもしれません。 –

+1

[可能なアプローチ](https://play.rust-lang.org/?gist=de697ed924eba97b96e8e705b31ca8b2&version=stable)個人的には、あなたのバージョン(ループ付き)が好きです。関数型のコードを書くことができますから* – trentcl

答えて

3

Questionmark-Operatorは、このコンテキスト(タプル内)では動作しないようです。

問題は?が故障した場合にErrを返し、あなたが成功した場合にはOkを返していなかったということです。あなたがそれを行うならば、オペレータはうまく動作します。その向こうには、空白で分割からのイテレータに操作することにより、Vecの余分な割り当てを避けることができます。

fn from_str(s: &str) -> Result<Self, Self::Err> { 
    let mut elems = s.split_whitespace(); 

    let num_tuples = elems.next().expect("error handling: count missing").parse()?; 
    let tuples: Vec<_> = elems 
     .by_ref() 
     .tuples() 
     .map(|(a, b, c)| Ok((a.into(), b.into(), c.parse()?))) 
     .take(num_tuples) 
     .collect::<Result<_, _>>()?; 

    if tuples.len() != num_tuples { panic!("error handling: too few") } 
    if elems.next().is_some() { panic!("error handling: too many") } 

    Ok(EnclosingObject { tuples }) 
} 

私もタプルに自動的にグループイテレータItertools' tuples方法を使用してResult<Vec<_>, _>に収集しました。構造体の冗長なtuples: tuplesを減らし、残りのエラー処理のためにいくつかのプレースホルダを追加しました。私はVec::with_capacityを削除しました。size_hintによって設定されたtakeが十分に良いと私は信じているからです。あなたがそれを信用しなかったならば、あなたはまだwith_capacityを使用してから、extendイテレータを使ってベクトルを使用することができます。

+0

Nitpicking:もし 'tuples.len()!= num_tuples'なら、それはあまりにも多すぎるかもしれません – Jmb

+2

@Jmb howそう?そのことを防ぐために「取る」ことはできないのですか? – Shepmaster

+0

あなたは正しいです、私は 'take'を逃しました、ノイズのために申し訳ありません – Jmb

関連する問題