Reputation: 635
I have this code for reading in a 2D vector. Is there a way to eliminate the need for temp_vec
?
let mut vec_size: usize = 3;
let mut vec = vec![vec![0; vec_size]; vec_size];
for i in 0..vec_size{
input = String::new();
io::stdin().read_line(&mut input).expect("Failed to read");
let temp_vec: Vec<i32> = input
.split_whitespace()
.map(|s| s.parse().unwrap())
.collect();
for j in 0..temp_vec.len(){
vec[i][j] = temp_vec[j];
}
}
Upvotes: 2
Views: 134
Reputation: 1119
I you remove the collect()
call, you end up with an iterator, which you can enumerate()
and just pass to the for loop:
use std::io;
fn main() {
let vec_size: usize = 3;
let mut vec = vec![vec![0; vec_size]; vec_size];
let mut input = String::new();
for i in 0..vec_size {
input.clear();
io::stdin().read_line(&mut input).expect("Failed to read");
let numbers = input
.split_whitespace()
.map(|s| s.parse().unwrap());
for (j, x) in numbers.enumerate() {
vec[i][j] = x;
}
}
}
This code also calls clear()
to clear the input buffer instead of assigning new string, so it makes just one additional allocation (you may benefit from it if you're reading a lot of small matrices).
(sidenote about your code: it's better to use .iter().enumerate()
than iterate on vectors indices, if you can)
When writing this answer I've misread the original question thinking that there was a stack-allocated matrix:
const VEC_SIZE: usize = 3;
let mut vec = [[0; VEC_SIZE]; VEC_SIZE];
If that was the case, I would recommend my solution, but since it's a Vec<Vec<i32>>
, I'd recommend @Shepmaster's one, as it's more idiomatic.
Upvotes: 3
Reputation: 432189
Map the lines of standard input as the rows, then map the numbers in each row as columns.
use std::io;
use std::io::prelude::*;
const SIZE: usize = 3;
fn main() {
let stdin = io::stdin();
let vec: Vec<Vec<i32>> = stdin.lock()
.lines()
.take(SIZE)
.map(|line| {
let line = line.expect("Unable to read line");
line.split_whitespace()
.take(SIZE)
.map(|s| s.parse().expect("Enable to parse number"))
.collect()
})
.collect();
println!("{:?}", vec);
}
Or if you don't care to panic:
use std::io;
use std::io::prelude::*;
use std::error::Error;
const SIZE: usize = 3;
fn main() {
let stdin = io::stdin();
let vec: Result<Vec<Vec<i32>>, _> = stdin.lock()
.lines()
.take(SIZE)
.map(|line| {
line.map_err(|e| Box::new(e) as Box<Error>)
.and_then(|line| {
line.split_whitespace()
.take(SIZE)
.map(|s| s.parse().map_err(|e| Box::new(e) as Box<Error>))
.collect()
})
})
.collect();
println!("{:?}", vec);
}
Addressing concerns from a comment:
Your code makes 2n+1 allocations (that's important if somebody's looking for performance
It's unclear what N
is here, but there should be a maximum of 3 vectors allocated and 3 items in each vector. The take
adapter will override the size_hint
to put a maximum of 3
and then collect
will use that hint when constructing each Vec
.
Using nested
Vec
s for matrices is an antipattern.
Absolutely, but that was what the original code did.
You "break" stdin – you can't reliably use it after calling
lock().lines()
I'm not sure what you mean here. I am able to add stdin.read(&mut[0,0,0]).expect("Unable to read more")
after the chunk of code that defines let vec
, showing that it can be used.
If there was a problem with not being able to use stdin, you could fix it by scoping the lock to a block that ends earlier.
Upvotes: 1