I need to compare a char to another char in same string and get total number of chars meeting that condition. I have currently implemented this in C++ style by converting string into array of u8 bytes and comparing using indices of elements and primitive for loop. Is there more idiomatic way of doing it ? Also can i do it in functional way using iterator adapters such as map,filter etc?
My current implementation
let s = "ABAAB".as_bytes();
let n= s.len();
let mut count = 0;
for i in 0..n / 2 {
if s[i] != s[n - i - 1] {
count += 1
};
}
println!("total matched chars: {}",count);
The main concern here is to compare ith char with n-i-1 th char and keep count of chars matching that condition. I would like to make it more concise and nicer like python implementation below:
count=sum(int(s[i] != s[n-1-i]) for i in xrange(n//2))
The bytes implementation is incorrect. Multi-byte chars will will break it.
let string = "abфПфba";
let mut count = 0;
let forward = string.char_indices();
let reverse = string.char_indices().rev();
for ((i, a), (j, b)) in forward.zip(reverse) {
if i >= j { break; }
if a == b { count += 1; }
}
dbg!(count);
Thank u so much for replying. No problem for multi-byte chars as I am only using ASCII chars. Its very neat and idiomatic.I was really struggling to express the logic in idiomatic way. Turns out I was only thinking in imperative way.
No problem for multi-byte chars as I am only using ASCII chars. Your solution is really neat especially using char indices to test breaking condition. I will definitely check it out in my code. Thank u so much for helping.
Suggestion then for your (internal) API: Make the function take &[u8], so it's clear from the signature that it won't handle multibyte chars properly (or at least, it's sort of implied), and it won't panic from slicing a multibyte char in half. The caller can then decide to use it with s.as_bytes(), and does have a choice what to do if the string had multibyte chars to begin with.
Ok I will do it if i will implement it in API. But for now I am just reading string from stdin. I think the given strings in my case will always be array of u8 bytes. I am using above code to solve this google kickstart problem using c++ solution given here . I was really struggling in processing strings in idiomatic way in rust. I only had experience doing it in imperative way in c++. Now I will try using above solutions.
If you find yourself wishing there were as many facilities for &[u8] as there are for str, I recommend the bstr crate. (Not always a possibility in coding competitions, being an external crate, though.)
Thanks for recommendation. yeah external crate might not be allowed but I will definitely check it and hope that I'll get some ideas on strings manipulation so that i could come up with my own logic.
Why not use str::split_at? Like let (left, right) = s.split_at(mid)? The only case I can think it won't work if it splits at utf-8 boundary but if it's ASCII this would work.
@s3bk's impl is just fine, but another way I'd imagine doing it is using the .chars() iterator and taking both .next() and .next_back() from the iterator in each iteration. Stop iteration if either end is empty. Indexes are not needed for this double-ended sequence.
pub fn matching_chars(s: &str) -> usize {
let mut chars = s.chars();
let mut matching = 0;
while let (Some(c1), Some(c2)) = (chars.next(), chars.next_back()) {
// unsure from given code and description if != or == is wanted here
matching += if c1 != c2 { 1 } else { 0 };
}
matching
}
fn main() {
println!("{}", matching_chars("ABAAB"));
println!("{}", matching_chars("ABAABA"));
}