백준 1001(ft. rust)

Hyeseong·2023년 5월 10일
0

rust bakjun algorithms

목록 보기
1/2

문제

두 정수 A와 B를 입력받은 다음, A-B를 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 A와 B가 주어진다.(0<A, B<10)

출력

첫째 줄에 A-B를 출력한다.

예제 입력 1

3 2

예제 출력 1

1

Case-1

use std::io;
fn main() {
    println!("Please enter numbers: ");
    let mut number = String::new();
    io::stdin().read_line(&mut number).unwrap();
    let numbers: Vec<&str> = number.split_whitespace().collect();
    let number_first = numbers[0].parse::<i32>().unwrap();
    let number_second = numbers[1].parse::<i32>().unwrap();

    println!("{}", number_first - number_second);
}

설명

  1. use std::io; std::io 모듈을 가져옵니다. 이 모듈은 표준 입출력을 포함한 여러 I/O 관련 기능을 제공합니다.

  2. fn main() {: Rust 프로그램의 메인 함수를 정의합니다. 모든 Rust 프로그램은 main 함수에서 시작됩니다.

  3. let mut input = String::new(); input이라는 이름의 가변(mut) String 변수를 생성하고 초기화합니다. 이 변수에 사용자의 입력을 저장할 것입니다.

    • 다른 방식
      - let mut input = String::with_capacity(50); 미리 정해진 용량으로 빈 문자열을 생성할 수 있습니다.
      - std::io::Read 트레이트의 read_to_string() 메서드를 사용할 수도 있습니다:
      use std::io::{self, Read};
      - read_to_string() 메서드는 표준 입력에서 데이터를 읽어서 문자열에 저장합니다. 이 방법은 입력 전체를 한 번에 읽습니다.
      이 경우, 입력 중간에 개행 문자가 있어도 전체 내용이 input 변수에 저장됩니다. 따라서 이 방법은 여러 줄의 입력을 처리할 때 유용할 수 있습니다.
  4. io::stdin().read_line(&mut input).expect("Failed to read line") :

    4.1. io::stdin() 메서드는 Rust의 표준 라이브러리인 std::io 모듈에서 제공하는 함수입니다. 이 메서드는 표준 입력 (stdin)을 나타내는 std::io::Stdin 타입의 인스턴스를 반환합니다. Stdin 인스턴스는 사용자로부터의 입력을 읽어들이는 데 사용됩니다.

    4.2. read_line() :
    - std::io::Stdin 타입의 인스턴스에서 제공되는 함수
    - 사용자로부터의 입력을 읽어들여 문자열에 저장
    - Result 타입을 반환
    - 입력 읽기 중 발생할 수 있는 오류를 처리

    4.3. unwrap() : Result 타입 또는 Option 타입의 값에 대해 호출할 수 있는 메서드.
    - Result : Result::Ok variant인 경우 내부의 값을 반환하고, Result::Err variant인 경우 패닉(panic)을 발생시켜 프로그램을 중단합니다.
    - Option : Option 타입의 경우, Option::Some variant인 경우 내부의 값을 반환하고, Option::None인 경우 패닉(panic)을 발생시켜 프로그램을 중단합니다.
    - 주로 디버깅이나 프로토타이핑에 사용되며, 실제 프로덕션 코드에서는 대신 match 문이나 if let 구문을 사용하여 에러 처리를 명시적으로 하는 것이 권장됩니다.

  1. let nums: Vec<i32> = input: nums라는 이름의 Vec<i32> 타입 변수를 선언하고, 아래의 메서드 체인을 사용하여 값을 할당합니다.

    5.1. rust에서 Vec과 같은 타입을 컬렉션이라함

    • 컬렉션: 여러 데이터를 저장하는 구조이며 동적으로 변함

    컬렉션 데이터 타입 종류
    1. Vec<T>: 동적 크기의 배열로, 연속된 메모리에 저장되어 있으며 인덱스를 사용하여 빠른 접근이 가능합니다. 요소를 추가하거나 제거할 때 크기가 동적으로 변경됩니다. 요소가 삽입되거나 제거될 때 다른 요소를 이동해야 하므로 뒷부분의 추가 및 제거 작업은 느릴 수 있습니다.
    2. LinkedList<T>: 연결 리스트로, 각 요소가 다음 요소에 대한 포인터를 가집니다. 이를 통해 요소의 추가 및 제거가 빠르지만 인덱스를 사용한 랜덤 액세스가 불가능하므로 선형 탐색이 필요하며 상대적으로 느립니다.
    3. HashMap<K, V>: 해시 테이블 기반의 키-값 쌍을 저장하는 무순서 집합입니다. 빠른 검색, 삽입 및 제거 작업이 가능하지만, 키에 대한 순서를 유지하지 않습니다.
    4. BTreeMap<K, V>: 이진 탐색 트리 기반의 키-값 쌍을 저장하는 정렬된 맵입니다. 키 순서를 유지하며 상대적으로 빠른 검색, 삽입 및 제거 작업이 가능합니다.
    5. HashSet<T>: 유일한 요소를 저장하는 무순서 집합입니다. 요소의 검색, 삽입 및 제거 작업이 빠르지만, 순서를 유지하지 않습니다.
    6. BTreeSet<T>: 유일한 요소를 저장하는 정렬된 집합입니다. 요소 순서를 유지하며 상대적으로 빠른 검색, 삽입 및 제거 작업이 가능합니다.

  • 이러한 컬렉션들은 std::collections 모듈에서 제공되며, 각각의 특성에 따라 사용 케이스에 맞게 선택하여 사용할 수 있습니다.

    Vec는 Rust에서 가장 기본적이고 자주 사용되는 컬렉션 타입 중 하나이므로, std::collections 모듈을 명시적으로 가져오지 않아도 사용할 수 있습니다. Vec는 표준 라이브러리의 prelude 부분에 포함되어 있어 자동으로 스코프에 포함되기 때문입니다.
    다른 컬렉션 타입들을 사용하려면 std::collections 모듈을 명시적으로 가져와야 합니다. 예를 들어, HashMap이나 HashSet을 사용하려면 다음과 같이 작성해야 합니다

use std::collections::HashMap;
use std::collections::HashSet;
  • Rust에서 타입을 명시하지 않고도 변수에 값을 할당 할 수 있습니다.

    • 컴파일러가 타입을 추론 할 수 있기 때문

    • 예: 변수를 초기화 할 때 컴파일러는 초기 값의 타입을 기반으로 변수의 타입을 추론합니다.

      let numbers = vec![1, 2, 3]; // 타입 추론에 의해 Vec<i32>
      let names = vec![String::from("Alice"), String::from("Bob"), String::from("Carol")]; // 타입 추론에 의해 Vec<String>  
    • 모든 경우에 타입 추론이 가능하지 않음.
      - 제네릭 함수에서 타입을 지정하지 않고 사용하려면 컴파일러가 타입을 추론할 수 있는 정보가 필요함.

    • 데이터 컬렉션의 타입을 명시하지 않고 사용하는 것은 불가능.

    • 컴파일러는 대부분의 경우 타입 추론을 사용하여 타입을 결정 할 수 있지만, 컬렉션의 경우에는 구체적인 타입을 제공해야함.

5.2. split_whitespace()

  • 반환 타입: std::str::SplitWhitespace

  • 반복자(iterator)

  • str 타입의 문자열에 적용되는 메서드

  • String 타입의 문자열에도 쉽게 사용

    • String 타입의 문자열str 타입의 문자열로 자동으로 참조될 수 있기 때문
      - 뭔말? : str 타입의 문자열로 자동으로 참조될 수 있기 때문

      String 타입과 str 타입은 서로 다른 문자열 타입입니다.
      String 타입은 동적으로 할당되고 변경 가능한 문자열이고, str 타입은 불변이고 고정된 크기를 가진 문자열입니다. String 타입의 문자열은 str 타입의 문자열로 자동으로 참조될 수 있다는 말은, String 타입의 문자열이 str 타입을 참조하는 슬라이스 형태로 자동 변환될 수 있다는 뜻입니다. 이 변환은 Rust에서 자동으로 처리해주기 때문에 프로그래머는 크게 신경 쓰지 않아도 됩니다.

  • 예시

    let s: String = String::from("Hello, world!");
    let r: &str = &s;
  • String 타입의 s 변수는 &str 타입의 r 변수에 대입됩니다. 이때 s가 자동으로 str 타입을 참조하는 슬라이스 형태로 변환되어 r에 대입됩니다. 이 변환 과정이 자동으로 이루어져서, 프로그래머는 따로 변환을 처리하지 않아도 됩니다. 이러한 특성 덕분에 split_whitespace와 같은 메서드를 String과 str 타입 모두에 사용할 수 있습니다.

5.3. collect(): 제네릭 메서드는 입력 타입과 반환 타입을 런타임에서 결정하기에 반환 타입 추론이 어려움.

  • 제네릭 타입 매개변수 B이며 FromIterator<Self::Item> 트레잇을 구현하는 타입이어야 함.
  • 실제 반환 타입은 함수가 호출되는 시점에 컴파일러에 의해 결정
  • 참고 : 제네릭 메서드를 확인 하는 방법
    - 함수 선언부에 꺽쇠괄호(<>), 타입 매개변수를 포함하고 있는지 아닌지를 통해 파악 할 수 있음.
    fn example_function<T>(input:T) -> T{
    }
  • 제네릭 메서드를 사용할 때는, 컴파일러가 타입을 추론할 수 있는 경우에는 굳이 타입을 지정해주지 않아도 되지만, 타입 추론이 불가능한 경우에는 개발자가 명시적으로 타입을 지정해주어야함. 이때, 개발자는 함 수 사용시 꺽쇠괄호 안에 원하는 타입을 지정 할 수 있음.
let result = example_function::<i32>(42);
let result = example_function(42); // 컴파일러가 i32 타입을 추론합니다.
  • 참고2 : :: 기호는 무엇인가?

    • 네임스페이스, 타입의 멤버에 접근 할 때 사용하는 연산자.
    1. 모듈, 구조체, 열거형 등의 네임스페이스에 속한 항목에 접근 시.
    mod example{
    	pub fn hello(){
    		println!("hello, world!");
    	}
    }
    fn main() {
    	example::hello();// 모듈 example의 hello 함수를 호출합니다.
    		}
    }
    1. 구조체나 열거형의 연관함수를 호출 시!
    struct Example {
    	value: i32,
    }
    
    impl Example {
    	fn new(value: i32) -> Example{
    		Example { value }
    	}
    }
    
    fn main() {
    	let example = Example::new(42)
    }
    1. 제네릭 함수에서 특정 타입을 명시적으로 지정할 때
    fn example_function<T>(input: T) -> T {
    	// 함수 구현
    }
    fn main() {
    	let result = example_function::<i32>(42);// 제네릭 함수에 명시적으로 i32 타입을 지정합니다. 
    }
    1. trait이 구현된 타입에 대한 메서드를 호출 시
    trait ExampleTrait{
    	fn hello(&self);
    }
    
    struct Example;
    
    impl ExampleTrait for Example {
    	fn hello(&self) {
    		println!("Hello, world!");
    	}
    }
    
    fn main() {
    	let example = Example;
    	ExampleTrait::hello(&example); // 
    }

6.let number_first = numbers[0].parse::<i32>().unwrap();

  • parse
    • 제네릭 함수
    • 변환 타입 명세 -> 그러면 해당 타입의 FromStr 트레잇이 구현되어 있는지 확인하고, 구현되어 있다면 문자열을 해당 타입으로 변환

unwrap 메서드 리팩토링

match

use std::io;

fn main() {
    println!("Please enter numbers: ");
    let mut number = String::new();
    io::stdin().read_line(&mut number).unwrap();
    let numbers: Vec<&str> = number.split_whitespace().collect();
    
    let number_first = match numbers[0].parse::<i32>() {
        Ok(num) => num,
        Err(_) => {
            println!("Failed to parse the first number");
            return;
        }
    };
    
    let number_second = match numbers[1].parse::<i32>() {
        Ok(num) => num,
        Err(_) => {
            println!("Failed to parse the second number");
            return;
        }
    };

    println!("{}", number_first - number_second);
}
  

if let

use std::io;

fn main() {
    println!("Please enter numbers: ");
    let mut number = String::new();
    io::stdin().read_line(&mut number).unwrap();
    let numbers: Vec<&str> = number.split_whitespace().collect();

    let number_first = if let Ok(num) = numbers[0].parse::<i32>() {
        num
    } else {
        println!("Failed to parse the first number");
        return;
    };

    let number_second = if let Ok(num) = numbers[1].parse::<i32>() {
        num
    } else {
        println!("Failed to parse the second number");
        return;
    };

    println!("{}", number_first - number_second);
}

Case-2

// 출처 : gpt-4
use std::io;

fn main() {
    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("Failed to read line");

    let nums: Vec<i32> = input
        .split_whitespace()
        .map(|s| s.parse().expect("Failed to parse integer"))
        .collect();

    let a = nums[0];
    let b = nums[1];

    println!("{}", a - b);
}

설명

  1. map()
  • 반환 타입: std::iter::Map
  • Iterator 트레잇에 정의된 메서드로 'Iterator`를 구현하는 모든 타입에서 사용 가능
  • 주어진 클로저를 이터레이터의 각 요소에 적용하여 새로운 이터레이터를 생성합니다.
    • 클로저: 함수와 유사한 동작을 하는 익명의 코드 블록
    • 변수들을 자동으로 캡쳐 -> 연산 수행
    • 함수 인자로 전달되거나 반환 값으로 사용

Case-3

use std::io;

fn main() {
    println!("Please enter numbers: ");
    let mut number = String::new();
    io::stdin().read_line(&mut number).unwrap();
    let numbers: Vec<&str> = number.split_whitespace().collect();

    let mut number_first: i32 = 0;
    let mut number_second: i32 = 0;
    for (index, num_str) in numbers.iter().enumerate() {
        let num = num_str.parse::<i32>().unwrap();
        if index == 0 {
            number_first = num;
        } else if index == 1 {
            number_second = num;
            break;
        }
    }

    println!("{}", number_first - number_second);
}
profile
어제보다 오늘 그리고 오늘 보다 내일...

0개의 댓글