How to simplify this?

I have this function:

pub fn two_sum(numbers: Vec<i32>, target: i32) -> Vec<i32> {
      let mut iter = numbers.iter().enumerate();
      
      if let Some((i, l)) = iter.next() {
          if let Some((j, r)) = iter.by_ref().rev().next() {
              if l + r == target {
                  vec![i as i32 + 1, j as i32 + 1]
              } else {
                  std::iter::repeat(()).try_fold((l + r > target, (i, l), (j, r)), move |(cond, (i, l), (j, r)), ()| {
                      if cond {
                          if let Some((j, r)) = iter.by_ref().rev().next() {
                              if l + r == target {
                                  Err(vec![i as i32 + 1, j as i32 + 1])
                              } else {
                                  Ok((l + r > target, (i, l), (j, r)))
                              }
                          } else {
                              Err(vec![])
                          }
                      } else {
                          if let Some((i, l)) = iter.next() {
                              if l + r == target {
                                  Err(vec![i as i32 + 1, j as i32 + 1])
                              } else {                                    
                                  Ok((l + r > target, (i, l), (j, r)))
                              }
                          } else {
                              Err(vec![])
                          }
                      }
                  }).err().unwrap_or(vec![])
              }
          } else {
              vec![]
          }
      } else {
          vec![]
      }
 }

Using for loops it can be simplified but, I want to be solved it ONLY using iterators. How to simplify only using iterators?

That's quite a complicated function. What does it do?

It solves this leetcode twosum problem.

I don't think there is any elegant solution using only iterators. I would use a while loop.

What I understood in iterators is it is really really hard to arbitrarily change direction while in the middle of iteration which can be seen in the above function. Is there any neat way to do this?

Here's what I would consider an elegant solution:

pub fn two_sum(numbers: Vec<i32>, target: i32) -> Vec<i32> {
    let len = numbers.len() as i32;
    let mut i = 0;
    let mut j = len-1;
    
    while i < len && j >= 0 {
        let current = numbers[i as usize] + numbers[j as usize];
        
        if current < target {
            i += 1;
        } else if current > target {
            j -= 1;
        } else {
            return vec![1+i, 1+j];
        }
    }
    
    vec![]
}

There are no elegant solutions that involve iterators.

5 Likes

Here is a version using iterators:

use std::cmp::Ordering;

pub fn two_sum(numbers: Vec<i32>, target: i32) -> Vec<i32> {
    let mut iter = numbers.iter().enumerate();
    let mut front = iter.next();
    let mut back = iter.next_back();
    while let (Some((a_idx, a)), Some((b_idx, b))) = (front, back) {
        match (a + b).cmp(&target) {
            Ordering::Less => front = iter.next(),
            Ordering::Greater => back = iter.next_back(),
            Ordering::Equal => return vec![(a_idx as i32) + 1, (b_idx as i32) + 1],
        }
    }
    panic!("no solution")
}
7 Likes