'러스트 박살내기(?)' 시리즈Rust-Lang으로 알고리즘 문제 풀기 (기초 다지기)

배진오

@baealex

소비적인 일보단 생산적인 일을 좋아합니다.

2019-12-10에 작성한 러스트와 관련된 첫 글인데 이번이 두번째 쓰는 글이다. 2-3일은 무슨... 3달이 훌쩍 지나버렸다.



맨날 '해야지 해야지' 하면서 '해야지'만 맨날 하는중이다. 정말 너란 녀석... 이제는 진짜 해야지! 진짜 진짜 스스로와의 약속을 하겠다. 죽이되든 밥이되는 무엇이건 러스트를 이용해서 해결할 것이다. 함수형과 러스트에 익숙해지기 위해서 2일에 하나씩 30일간 15개의 개시글을 꼭 올린다! 차후엔 러스트를 이용해서 실사용 가능한 서비스를 만들고 싶다. 기존것을 러스트로 바꿔보던지. 이유는 없다 그냥 재밌어 보이기 때문에 도전하는 것이다!


두 수 비교하기

첫째 줄에 다음 세 가지 중 하나를 출력한다.

  • A가 B보다 큰 경우에는 '>'를 출력한다.
  • A가 B보다 작은 경우에는 '<'를 출력한다.
  • A와 B가 같은 경우에는 '=='를 출력한다.

입력과 비교, 출력을 수행하는 아주 기초적이면서 훌륭한 문제라고 생각한다.

use std::io;

fn main() {
    let mut input_number = String::new();

    io::stdin().read_line(&mut input_number)
        .expect("Falied to read line");
}

입력은 위와같이 받았던 걸로 기억하는데 스플릿은 어떻게 하는거지?

let split_input_number = input_number.split(' ');
for number in split_input_number {
    println!("{}", number);
}

문서가 어려워 보였던거지 사실은 일반적인 언어들과 비슷하게 위와같이 잘라낼 수 있다. 다만 참고한 스택오버플로에선 mut로 선언하였는데 컴파일시 mut로 선언한 것이 위험하다고 알려줬다. 하긴 스플릿된 변수를 굳이 mut로 선언할 필요는 없어보이긴 한다.

use std::io;

fn main() {
    let mut input_number = String::new();

    io::stdin().read_line(&mut input_number)
        .expect("Falied to read line");

    let split_input_number = input_number.split(' ');
    for number in split_input_number {
        println!("{}", number);
    }
}
45 21
45
21

이제 위와같이 두개의 숫자를 분할하였으니 비교를 진행하자.






잠깐...






split_input_number에 어떻게... 접근하는 거지...

use std::io;

fn main() {
    let mut input_number = String::new();

    io::stdin().read_line(&mut input_number)
        .expect("Falied to read line");

    let strings: Vec<&str> = input_number.split_whitespace().collect();
    println!("{:?}", strings);
}
45 21
["45", "21"]

스플릿한 결과가 iterator를 반환하므로 아까 위와같이 for i in items로 사용한 거였다. 이걸 배열로 받으려면 위와같이 collect를 사용해서 얻어야 한다고 설명한다. 이제 형변환을 해줘야 할 차례!

use std::io;

fn main() {
    let mut input_number = String::new();

    io::stdin().read_line(&mut input_number)
        .expect("Falied to read line");

    let numbers: Vec<&str> = input_number.split_whitespace().collect();

    let number_a = match numbers[0].parse::<i32>() {
        Ok(i) => i,
        Err(_e) => {
            -1
        }
    };
    let number_b = match numbers[1].parse::<i32>() {
        Ok(i) => i,
        Err(_e) => {
            -1
        }
    };
    println!("{}", number_a);
    println!("{}", number_b);
}

이제 비교만 해주면 끝이구나...

use std::io;
use std::cmp::Ordering;

fn main() {
    let mut input_number = String::new();

    io::stdin().read_line(&mut input_number)
        .expect("Falied to read line");

    let numbers: Vec<&str> = input_number.split_whitespace().collect();

    let number_a = match numbers[0].parse::<i32>() {
        Ok(i) => i,
        Err(_e) => {
            -1
        }
    };
    let number_b = match numbers[1].parse::<i32>() {
        Ok(i) => i,
        Err(_e) => {
            -1
        }
    };
    match number_a.cmp(&number_b) {
        Ordering::Less      => println!("<"),
        Ordering::Greater   => println!(">"),
        Ordering::Equal     => {
            println!("==");
        },
    }
}

난생처음 러스트로 문제를 풀어보았다. 문법 적응이 너무 안된다. 한문제를 더 풀어보자.


상근날드

가장 적응이 안되는 문법은 cmpOrdering이다. 러스트에는 if문이 없는건가?

를 찾다가 위와같이 문법이 정리된 페이지를 발견했다. 싹 정리 되있어서 찾아보고 싶은건 금방 찾아볼 수 있겠다. 여하지간 if/else님께서도 아주 잘 계셨다. 왜 튜토리얼에선 if/else가 아닌 cmpOrdering을 먼저 알려준;

여하지간 상근날드라는 문제는 5개의 입력을 받는데 3개는 햄버거 가격이고 2개는 음료수 가격이다 각각 물건중 저렴한 물건을 구매하여 합산한 가격에 50원을 빼는 문제다. 배열과 반복문 조건문을 활용해야 하는 간단한 문제다.

use std::io;

fn input_int() -> i32 {
    let mut temp_str = String::new();
    io::stdin().read_line(&mut temp_str)
        .expect("Falied to read line");
    let result = match temp_str.trim().parse::<i32>() {
        Ok(i) => i,
        Err(_e) => {
            -1
        }
    };
    return result;
}

정수 입력 받는게 귀찮아서 위와같이 함수로 만들었다.

fn main() {
    let mut menus: [i32; 5] = Default::default();

    let mut optional = Some(0);
    while let Some(i) = optional {
        if i < menus.len() {
            optional = Some(i + 1);
            menus[i] = input_int()
        } else {
            optional = None;
        }
    }
    let bergers = &menus[0 .. 3];
    let drinks = &menus[3 .. 5];

    println!("{:?}", bergers);
    println!("{:?}", drinks);
}

진입점에선 위와같이 루프를 돌리며 메뉴 배열에 값을 채우고 버거와 음료를 나눠주었다. 이제 이걸 정렬해서 앞에것만 가져오려고 하였더만 정렬을 사용하려면 백터로 선언하는게 일반적인 것 같다.

let mut bergers = vec![&menus[3 .. 5]];
bergers[0].sort(); // Error

let mut bergers = Vec::new();
bergers.extend(&mut menus[0 .. 3].iter().cloned());
bergers.sort();

백터를 생성하면서 munus를 넣어주려 했더니 2차원 배열로 만들어지고 정렬하려고 시도하자 mut 변수가 아니라고 오류를 뿜어냈다. 그래서 위와같이 작성하였다.

use std::io;

fn input_int() -> i32 {
    let mut temp_str = String::new();
    io::stdin().read_line(&mut temp_str)
        .expect("Falied to read line");
    let result = match temp_str.trim().parse::<i32>() {
        Ok(i) => i,
        Err(_e) => {
            -1
        }
    };
    return result;
}

fn main() {
    let mut menus: [i32; 5] = Default::default();

    let mut optional = Some(0);
    while let Some(i) = optional {
        if i < menus.len() {
            optional = Some(i + 1);
            menus[i] = input_int()
        } else {
            optional = None;
        }
    }

    let mut bergers = Vec::new();
    bergers.extend(&mut menus[0 .. 3].iter().cloned());
    bergers.sort();

    let mut drinks = Vec::new();
    drinks.extend(&mut menus[3 .. 5].iter().cloned());
    drinks.sort();

    println!("{:?}", bergers[0] + drinks[0] - 50);
}

파이썬이면 5줄이면 짤 코드가 이렇게 길어지니 왠지 바보가 된 느낌이다. 이 또한 문법이 익숙해 지면서 차차 나아질 것이라 생각된다. 오늘은 러스트의 기본기(?)를 다졌으니 다음에는 파이썬으로 풀었던 코드를 러스트로 바꿔보는 시도를 해봐야겠다.

fmowl
3개월전

let input : Vec<i32> = input.trim().split_whitespace().map(|x| x.parse().unwrap()).collect(); 이렇게하면 한 번에 파싱, 벡터에 넣을 수 있습니다.

baealex
3개월전

@fmowl 러스트 문법은 참 익숙해 지지가 않네요... 친절하게 알려주셔서 감사합니다 👍 더 열심히 공부해야 겠어요 😁

댓글을 작성하기 위해 로그인이 필요합니다.