며칠전에 닷넷 컨퍼런스를 통해서 닷넷 5.0이 공개되었다. 오늘자 컨퍼런스를 보니 이번 5.0에서는 획기적인 개선이 있는 것 같다. static void Main 없이 소스코드를 바로 써내려갈 수 있게 한다는 내용을 보면 파이썬이나 루비같은 언어의 스타일을 도입하는 것 같다.

 

C 샤프의 디자이너 Mads는 이 방식이 더 많은 사람들이 쉽게 배울 것이라고 생각하는 것 같다.

 

맞다. static void Main 이거 은근히 진입 장벽이었으리라. 90년대 C언어 부터 다뤄오던 숙련자들에겐 별 문제되지 않을 것인데 새로운 학습자들 (게다가 젊기 까지한)을 확보하는 것은 언어의 미래를 위해서 중요하다.

 

특히나 몇년 사이 파이썬이 두각을 나타내고 있으니 의식하지 않을 수 없다.

 

한편 마이크로 소프트의 제품들을 통합하는 C#은 인터프리터인 파이썬 보다 훨씬 복잡한 생태계를 가지고 있으므로 그렇게 쉽게 마음대로 언어의 규칙을 바꿀 수 있는 것이 아니다. 많은 고심이 있었을 것이다. 당분간 지켜봐야겠지만 어쨋든 마이크로 소프트의 대규모 선전으로 C#을 다시 잡을 의미가 생겼다.

 

What’s New in C#?

 

 

마이크로소프트, 차세대 통합 개발 플랫폼 닷넷5 출시

마이크로소프트(MS)가 통합 개발환경을 지원하는 차세대 애플리케이션 플랫폼 닷넷5.0(.NET 5.0)을 출시했다.미국 지디넷은 마이크로소프트가 닷넷5.0을 정식 출시했다고...

zdnet.co.kr

C#에 대하여는 아직 많이 다루지 못했는데 MS의 시대와 컴퓨팅월드를 같이 살아온 필자는 항상 관심을 가지고 있는 언어다. 닷넷 5.0에 대하여는 차후에 다뤄보기로 하고 이번 포스팅에서는 C# 튜토리얼을 계속 이어 나간다.

 

for 루프로 구구단 출력하기

        static void Main(string[] args)
        {
            WriteLine("Hello World!\n");
            int level = 9;
            MultiTable(level);
        }

        static void MultiTable(int length)
        {
            for (int i = 2; i <= length; i++)
            {
                for (int j = 1; j < 10; j++)
                {
                    Write("{0, 2} x{1, 2} = {2, 2} |", i, j, i*j);
                }
                WriteLine();
            }
        }

for 문 연습문제 중에 구구단 출력이 제일 쉽다.

 

구구단은 2단부터 시작해서 9단에서 끝난다. 그러므로 i = 2 에서 시작하고 끝나는 단은 level 로 사용자가 조작가능하다

 

2 1 은 2 (이일은이) 부터 시작해서 9 9 81 (구구팔십일)에 끝난다.

 

콘솔창에서 줄을 맞추는게 좀더 깔끔하게 보이는데 Write 메서드의 포맷기능을 사용한다. {0, 2} 0 은 변수 i 이고 2는 오른쪽 2칸 정렬을 의미한다. 81까지니까 2칸까지만 하고 나머지는 공백으로 조절한다.

 

몇번 움직여 보다보면 이해할 것이다. 구구단은 다른게 없고 줄을 잘 맞춰 출력하는게 중요하다.

 

for 루프로 삼각형과 사각형 그리기

 

 

for 루프 삼각형은 언어를 한개라도 배워본 사람이라면 만들어 봤을 것이다.

 

가장 기초적인 알고리즘의 하나인데 생각보다 재미있다. 블럭쌓기의 기초정도로 볼 수 있다.

 

for 루프로 도형을 표현할 수 있다는 것은 화면에 무엇인가 출력하는 개념을 알게 된다.

 

삼각형을 * 별모양으로 그리는게 고전의 연습문제인데 포인트는 * 별모양에 집중하지 말고 숫자로 보는 것이다.

 

*아래의 별모양을 보면 막연하다.

*숫자로 파악하면 의미를 파악하기가 쉽다.

보는 시각이 중요하다는 것이다. * 별모양의 보여지는 부분에 집중하다보면 너무 직관적이 되버려서 코드를 잘 짜기 어렵다.

 

두번째 그림에서 어떤 특징을 잡았는가? 그렇다 위에서부터 계단이라고 생각하면 1층에 올라갈 때는 한걸음, 2층에는 2걸음 ... 9층에는 9걸음이 필요하다.

 

C#의 문법은 어떤가? 일단 1층부터 9층까지는 이것이다.

            for (int i = 1; i <= 9; i++)
            {

            }

그런데 이렇게만 루프를 돌면 1층에 하나의 행동밖에 못한다. for 1번에 하나의 행동이다.

 

1층에는 1걸음 2층에 2걸음... 9층에 9걸음 가야한다고 하니 한 층에서 여러번 행동하도록 그 안에 for문을 작성한다.

 

            for (int i = 1; i <= 9; i++)
            {
                for (int j = 1; j <= i; j++)
                {
                    Console.Write(j + " ");
                }
                Console.WriteLine();
            }

이것을 for 중첩문이라고 한다. 한바퀴 도는 그 하나의 요소에 다시 한바퀴를 도는 것이다. 2차원 그래픽이 그려지는 것은 이게 2차원의 속성을 가지고 있기 때문이다.

 

한층을 의미하는 변수는 i 이므로 그 안의 for 루프에서는 i 까지 돌면된다. 한층이 끝나면 라인구분을 해줘야 하니 WriteLine 함수를 사용한다.

 

여기서 출력하는 문자는 변수의 값을 사용했는데 이것을 별모양이나 다른 어떤 모양으로 바꿔도 된다.

 

문자가 아니라 그 위치에서 이미지로 대체하면 컴퓨터 그래픽인 것이다.

 

그래서 for 루프의 훈련은 마치 미국인들이 좋아하는 영화 스타워즈의 광선검 훈련을 하는 것 처럼 고전이 되어버린 것이다. 스타워즈의 마스터들이 그렇듯 왜 이걸 해야하는지 같은 중요한 이유는 잘 안 알려주니까 불만일 수 있다.

 

원래 제다이 스승들은 일부러 안알려준다. 나중에 니가 알게 되리라. "May the force be with you."

 

영미권 프로그래머 중에 그런 의식을 가진 사람들이 꽤 있는 것 같다. 물론 콰이곤이나 오비완같이 친절한 스승도 많다. 하지만 기본적으로 프로그래밍은 누가 떠다먹인다고 되는게 아니라 자립하는 것이라 스스로 깨닫기를 바란다.

 

제다이 스승 요다

 

*이제 전체 예제 코드를 볼 시간이다.

 

using System;

namespace MyProject1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!\n");

            int level = 9;
            char ch1 = '0';

            Triangle1(level);
            Triangle2(level);
            Square(level);
            PrintLine2(1, level, ch1);
            PrintLine3(1, level, ch1);
            PrintLine4(1, level, ch1);
            PrintLine5(1, level, ch1);

        }

        static void Triangle1(int n)
        {
            Console.WriteLine();
            for (int line = 1; line <= n; line++)
            {
                PrintLine1(1, line);
            }
        }
        static void Triangle2(int n)
        {
            Console.WriteLine();
            for (int line = 1; line <= n; line++)
            {
                PrintLine1(1, line);
            }
            for (int line = n - 1; line >= 1; line--)
            {
                PrintLine1(1, line);
            }
        }
        static void Triangle3(int n)
        {
            Console.WriteLine();
            for (int line = 1; line <= n; line++)
            {
                PrintLine1(1, line);
            }
            for (int line = n - 1; line >= 1; line--)
            {
                PrintLine1(1, line);
            }
        }

        static void Square(int n)
        {
            Console.WriteLine();
            for (int i = 1; i <= n; i++)
            {
                for (int j = 1; j <= n; j++)
                {
                    Console.Write(i + " ");
                }
                Console.WriteLine();
            }
        }
        static void PrintLine1(int start, int end)
        {
            for (int i = start; i <= end; i++)
            {
                Console.Write(i + " ");
            }
            Console.WriteLine();
        }
        static void PrintLine2(int start, int end, char ch)
        {
            Console.WriteLine();
            for (int i = start; i <= end; i++)
            {
                for (int j = 1; j <= i; j++)
                {
                    Console.Write(ch + " ");
                }
                Console.Write(" ");
                for (int k = end; k-i >= 1; k--)
                {
                    Console.Write(k + " ");
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }        
        
        static void PrintLine3(int start, int end, char ch)
        {
            Console.WriteLine();
            for (int i = start; i <= end; i++)
            {
                for (int k = end; k-i >= 1; k--)
                {
                    Console.Write(k + " ");
                }
                Console.Write(" ");
                for (int j = 1; j <= i; j++)
                {
                    Console.Write(ch + " ");
                }
                
                Console.WriteLine();
            }
            Console.WriteLine();
        }        
        
        static void PrintLine4(int start, int end, char ch)
        {
            Console.WriteLine();
            for (int i = start; i <= end; i++)
            {
                for (int k = end; k-i >= 1; k--)
                {
                    Console.Write(k + " ");
                }
                Console.Write(" ");
                for (int j = 1; j <= i; j++)
                {
                    Console.Write(ch + " ");
                }                
                Console.Write(" ");
                for (int l = end; l >= i; l--)
                {
                    Console.Write(ch + " ");
                    //Console.Write(i);
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }        
        
        static void PrintLine5(int start, int end, char ch)
        {
            Console.WriteLine();
            for (int i = start; i <= end; i++)
            {
                for (int k = end; k-i >= 1; k--)
                {
                    Console.Write(k + " ");
                }
                Console.Write(" ");
                for (int j = 1; j <= i; j++)
                {
                    Console.Write(ch + " ");
                }                
                Console.Write(" ");
                for (int l = 1; l <= i; l++)
                {
                    Console.Write(ch + " ");
                    //Console.Write(i);
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }

    }

}

 

 

 

 

맨 위에서 부터 본다.

 

스태틱 메서드를 사용해서 약간 헷갈릴 수 있다.

 

Triangle1 과 2는 PrintLine 함수를 루프한다. PrintLine 함수는 단순 루프이다. 루프안에 루프하는 함수를 넣으니 중첩 루프문이다.

 

다음에 PrintLine2 부터 5 까지 단계적으로 보면 삼각형이 사각형이 되고 다시 사각형을 나누는 알고리즘이 들어있다.

 

Main 메서드에서 level 과 ch1 을 변경시켜서 출력해보면 어떤 차이가 있는지 알 수 있다.

 

이 그림으로 설명하면 숫자로 된 부분을 보면 알 수 있다 9에서 시작해서 내려온다. 그런데 1 바로 위인 2까지 내려온다.

 

9, 8, 7 ... 2 까지 하나씩 빼서 내려가는 코드가 필요하다. 아까 위에서는 1부터 9까지 올라갔다. 이의 반대로 생각하면 된다.

 

        static void PrintLine5(int start, int end, char ch)
        {
            Console.WriteLine();
            for (int i = start; i <= end; i++)
            {
                for (int k = end; k-i >= 1; k--)
                {
                    Console.Write(k + " ");
                }
                Console.Write(" ");
                for (int j = 1; j <= i; j++)
                {
                    Console.Write(ch + " ");
                }                
                Console.Write(" ");
                for (int l = 1; l <= i; l++)
                {
                    Console.Write(ch + " ");
                    //Console.Write(i);
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }

PrintLine5 의 중첩된 첫 for 문을 보면 k를 처음에 end로 설정한다. end는 전체 층의 높이다. 만약 9를 넣었다면 9부터 시작해서 내려온다. i는 1부터 시작하는 바깥쪽 루프에서 온다. 즉 i루프가 한번 회전할 때 9-1, 9-2, 9-3 ... 이렇게 루프가 진행된다. k 는 end에서 시작해서 (9이면) k-i 만큼 루프하니까 그 숫자 만큼 루프가 이루어진다. 처음에 그린 삼각형의 역삼각형이 된다.

 

(k-i)

8

7

6

5

4

3

2

1

 

그 다음 for문은  j=1에서 부터 j <= i 까지 돌면된다. i는 계속 증가하므로 밑으로 올라가는 삼각형의 모습이다. 첫번째와 두번째를 다 출력시키면 사각형을 두개로 쪼개놓은 모양이다.

 

마지막 삼각형도 두번째 삼각형을 한번 더 반복한다.

 

이런식으로 반복할 수 있다. 사람은 지치지만 컴퓨터는 지치지 않는다. for 루프문을 적당히 만들어 놓으면 이 작업을 1억이라도 수행시킬 수 있다.

 

여기서 만든 삼각형은 정사각형을 나눈 직각삼각형에 가깝다. 숫자간의 간격때문에 출력 콘솔에 따라 직사각형이 되지는 않을 것이지만 직각삼각형이 가진 특징을 가지고 있다. 응용은 언제나 가능하다.

 

콘솔로 그래픽을 표현하는 아스키아트가 가능한 것은 텍스트에 이미 그래픽적 요소(도형)가 담겨있기 때문이다. 단지 게임같이 상업용으로 만들기엔 가성비가 좋지 않아서 그런거지. 이런 것을 취미생활로 삼는 사람들이 세계엔 많다;;;

세상은 넓고 덕후들은 많다

 

아스키 아트

C# 기초 시리즈는 다음 포스팅에서 이어진다.

공유하기

facebook twitter kakaoTalk kakaostory naver band