ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 0. 문자열을 정수로 바꾸기
    코딩 테스트/Level 1 2019. 10. 2. 06:36
    반응형

    https://programmers.co.kr/learn/courses/30/lessons/12925

     

    코딩테스트 연습 - 문자열을 정수로 바꾸기

    문자열 s를 숫자로 변환한 결과를 반환하는 함수, solution을 완성하세요. 제한 조건 s의 길이는 1 이상 5이하입니다. s의 맨앞에는 부호(+, -)가 올 수 있습니다. s는 부호와 숫자로만 이루어져있습니

    programmers.co.kr

     

    프로그래머스 코딩 테스트 연습문제 중 쉬운 문제부터 풀어보겠습니다.

    문제 내용은 위 링크를 참고하세요.

     

    파이썬

    내장함수를 사용해서 푸는 것이 실용적입니다.

    def solution(s):
        return int(s)

    아래 코드는 PEP8 위반입니다. 실행은 됩니다. ㅎ

    solution = lambda s: int(s)

    PEP8을 봅시다... 

    https://www.python.org/dev/peps/pep-0008/

    lambda로 검색해보면...

    Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier:

    # Correct:
    def f(x): return 2*x

    # Wrong:
    f = lambda x: 2*x

     

     

    '알고리듬'스러운 방법으로 풀어봅시다. 

     

    숫자만 있는 경우라면 이런 방법을 쓸 수가 있겠죠. 

    ord('0')을 이용하면 굳이 아스키 코드를 외우지 않아도 풀 수 있죠. 
    70년대도 아니고 이런 문제 하나 풀기위해 아스키 코드 외울 필요까지는 없을 것 같습니다. 

    def solution(s):
        result = 0
        for index, each in enumerate(s[::-1]):
            result += (ord(each) - ord('0')) * 10 ** index
        return result

     

    같은 코드입니다. 리스트 컴프리헨션을 이용해 줄여보았습니다. 

    C파이썬에서는 보통 for 문 보다 리스트 컴프리헨션의 실행속도가 더 빠르기 때문에 이렇게도 자주 씁니다. 

    def solution(s):
        return sum([(ord(each) - ord('0')) * 10 ** index for index, each in enumerate(s[::-1])])

    이 문제 풀이와는 큰 상관없는 이야기입니다만,
    파이썬에서 리스트는 수정이 필요할 때만 씁시다.

    수정이 필요없을 때는 튜플을 쓰는 것이 더 빠릅니다. 

     

    이 경우 리스트 컴프리헨션보다 제너레이터 익스프레션을 쓰면 더 깔끔합니다. 

    제너레이터 익스프레션을 처음 봤을 때 '튜플 컴프리헨션'인가라는 생각이 들었습니다.  

    리스트는 한번에 처리해서 저장공간에 보관합니다. 재사용이 가능합니다. 
    제너레이터는 저장공간을 사용하지 않고, (요청이 들어오면) 저 값을 하나씩 생성(계산)해서 꺼내줍니다. 
    값을 넘겨주고 나면 끝입니다. 다시 꺼낼 수 없습니다. 이것을 소비한다고 표현하죠. 
    그래서 큰 저장공간을 사용하지 않습니다.

    조금 어려운 이야기니까 나중에 제너레이터를 배우게 될 때 한 번 더 생각해 봅시다. 

    def solution(s):
        return sum((ord(each) - ord('0')) * 10 ** index for index, each in enumerate(s[::-1]))

     

    함수형 프로그래밍으로 작성해도 좋습니다...

    from functools import reduce
    
    
    def solution(s):
        return reduce(lambda x, y: x * 10 + y, map(lambda x: ord(x) - ord('0'), s))

    코드가 돌아가는 구조를 보면 뭔가 제너레이터 익스프레션 또는 리스트 컴프리헨션과 비슷한 느낌이 듭니다.
    예전 파이썬에는 리듀스가 내장 함수로 있었는데, 지금은 functools로 빠졌는데요. 
    제너레이터 익스프레션 또는 리스트 컴프리헨션을 쓰면 거의 비슷하니까? 라는 이유가 아닐까 생각해 봅니다. 

     

    부호가 있는 경우까지

    제너레이터 익스프레션에서 인덱스만 살짝 바꿔서 구분해 보았습니다.

    def solution(s):
        if s[0] == '-':
            return -sum((ord(each) - ord('0')) * 10 ** index for index, each in enumerate(s[:0:-1]))
        elif s[0] == '+':
            return sum((ord(each) - ord('0')) * 10 ** index for index, each in enumerate(s[:0:-1]))
        else:
            return sum((ord(each) - ord('0')) * 10 ** index for index, each in enumerate(s[::-1]))

    줄 수는 늘어나지만 반복을 줄이는 것이 더 좋은 코드입니다. 

    def total(s):
        return sum((ord(each) - ord('0')) * 10 ** index for index, each in enumerate(s[::-1]))
    
    
    def solution(s):
        if s[0] == '+':
            return total(s[1:])
        elif s[0] == '-':
            return -total(s[1:])
        else:
            return total(s)

    삼항연산자를 겹쳐 쓰면 가독성이 안습이 됩니다. 

    def total(s):
        return sum((ord(each) - ord('0')) * 10 ** index for index, each in enumerate(s[::-1]))
    
    
    def solution(s):
        return total(s[1:]) if s[0] == '+' else (-total(s[1:]) if s[0] == '-' else total(s))

    조금이나마 보기 좋게...

    def total(s):
        return sum((ord(each) - ord('0')) * 10 ** index for index, each in enumerate(s[::-1]))
    
    
    def solution(s):
        return (-1 if s[0] == '-' else 1) * total(s[1:] if s[0] == '+' or s[0] == '-' else s)

     

    보기 좋은 코드는 아닙니다.

    코드 뽐내기(?)용 한 줄 집착

    def solution(s):
        return (-1 if s[0] == '-' else 1) * sum(
        (ord(each) - ord('0')) * 10 ** index 
        for index, each in enumerate((s[1:] if s[0] == '+' or s[0] == '-' else s)[::-1]))
    def solution(s):
        return (-1 if s[0] == '-' else 1) * sum(
            (ord(each) - ord('0')) * 10 ** index for index, each in
            enumerate(s[::-1]) if '0' <= each <= '9')

     

    하지 말라는 거 다 해보면 이렇게도 쓸 수 있습니다. 

    solution = lambda s: (-1 if s[0] == '-' else 1) * sum(
            (ord(each) - ord('0')) * 10 ** index for index, each in
            enumerate(s[::-1]) if '0' <= each <= '9')

     

    함수형으로는...

    from functools import reduce
    
    
    def solution(s):
        return (-1 if s[0] == '-' else 1) * reduce(lambda x, y: x * 10 + y, map(lambda x: ord(x) - ord('0'), filter(lambda x: '0' <= x <= '9', s)))

     

    자바스크립트

    function solution(n){
        return Number(n);
    }
    let solution = (n) => Number(n);

    자바

    class Solution {
        public int solution(String s) {
            return Integer.parseInt(s);
        }
    }

    import "strconv"
    
    func solution(s string) int {
    	num, _ := strconv.Atoi(s)
    	return num
    }

    코틀린

    class Solution {
        fun solution(s: String): Int {
            return Integer.parseInt(s)
        }
    }

    코틀린은 리턴 문을 함수의 시그니쳐에 작성할 수 있습니다. 

    class Solution{
        fun solution(s: String): Int = Integer.parseInt(s)
    }

    이때 타입 유추가 가능하면 타입을 생략할 수 있습니다. 

    class Solution{
        fun solution(s: String) = Integer.parseInt(s)
    }

    코틀린에서는 toInt() 메서드를 씁니다. 

    class Solution{
        fun solution(s: String) = s.toInt()
    }

    C#

    using System;
        public class Solution
        {
            public int solution(string s)
            {
                return Int32.Parse(s);
            }
        }

    C# 6.0 부터는 '식 본문 멤버'가 생겨 간단하게 쓸 수 있습니다.

    docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members

    public class Solution {
        public int solution(string s) => int.Parse(s);
    }

    인라인이 되는 걸 알았으니.. LINQ를 이용해 함수형으로 풀어봅시다. 앞에 '+, -' 만 아니었어도 깔끔하게 가능했는데...

    using System.Linq;
    
    public class Solution
    {
        public int solution(string s) =>
            s[0] == '-' ? -s.Substring(1).Select(x => (char)x - '0').Aggregate((x, y) => x * 10 + y)
            : s[0] == '+' ? s.Substring(1).Select(x => (char)x - '0').Aggregate((x, y) => x * 10 + y)
                : s.Select(x => (char)x - '0').Aggregate((x, y) => x * 10 + y);
    }

    재미로 작성해 본 코드입니다. 하지만 코테에서는 이런 코드가 더 인기있더라구요. 앞으로도 종종 써보겠습니다. 

    RUST

    fn solution(s: &str) -> i32 {
        s.parse().unwrap()
    }
    fn solution(s: &str) -> i32 {
        let mut result = 0;
        for (i, each) in s.chars().enumerate() {
            result = result
                + i32::pow(10, (s.len() - i - 1) as i32)
                    * match each {
                        '0' => 0,
                        '1' => 1,
                        '2' => 2,
                        '3' => 3,
                        '4' => 4,
                        '5' => 5,
                        '6' => 6,
                        '7' => 7,
                        '8' => 8,
                        '9' => 9,
                        _ => todo!("todo"),
                    }
        }
        result
    }

    DART

    int solution(s) {
      return int.parse(s);
    }

     

    SWIFT

    스위프트의 언래핑에 익숙해져야한다. 

    func solution(_ s:String) -> Int {
        return Int(s) ?? 0
    }
    func solution(_ s:String) -> Int {
        return Int(s)!
    }
    func solution(_ s:String) -> Int {
        if let num = Int(s) {
            return num
        }
        return 0
    }
    func solution(_ s:String) -> Int {
        guard let num = Int(s) else {
            return 0
        }
        return num
    }
    반응형
Designed by Tistory.