콘솔에 삼각형

         *
       * *
      *   *
     *     *
    *       *
   *         *
  *           *
 *             *
*****************


을 출력하는 Go 언어 컨솔 애플리케이션을 만들어 보자. 이런 소스 코드의 작성은 학원이나 학교에서 프로그래밍 입문자에게 과제로 많이 주어지는 것 중의 하나이다. 코끼리를 보거나 만진 사람들이 저마다 그 생김새를 말할 때 제각기 다르게 표현할 수 있듯이 이런 소스 코드의 작성도 알고 보면 얼마든지 많은 방법이 있을 것이다. 여기서는 쉬운 코드 부터 작성해 보고 차츰차츰 소스를 바꾸어 가면서 Go 언어 프로그래밍의 기초부분을 터득해 보기로 한다.

삼각형 출력 부분을 main() 함수에서 하지 않고, 별도로 구현된 printTriange() 함수에서 하기로 한다.

우선 첫번 째 예제는 Go 언어의 컨솔 출력 함수 fmt.Printf() 의 사용법 만 알면 누구나 코딩할 수 있는 매우 단순한 소스 코드이다.


삼각형 출력 예제 1
/*
 *  Filename: printTriangle1.go
 *            Print a triangle on console.
 *
 *  Execute: go run printTriangle1.go
 *  
 *  or
 *  
 *  Compile: go build printTriangle1.go
 *  Execute: ./printTriangle1
 *
 *      Date:  2012/06/26
 *    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]
 */

package main

import (
    "fmt"
)

func println(s string) {
    fmt.Printf("%s\n", s)
}

func print(s string) {
    fmt.Printf("%s", s)
}

func printTriange() {
    println("        *        ")
    println("       * *       ")
    println("      *   *      ")
    println("     *     *     ")
    println("    *       *    ");
    println("   *         *   ")
    println("  *           *  ")
    println(" *             * ")
    println("*****************")
}

func main() {
    printTriange()
}




위의 소스 코드는 아무 알고리즘도 없는 너무 단순한 코드이다. 이런 코드를 작성했다간 출력 모양이나 크기를 변경해야 하는 상황을 맞이하면 위드프로세서로 문서 만드는 것 이상으로 많은 수작업을 하거나 아니면 포기하는 지경에 이를 수도 있다. 그래서 다음 처럼 좀 더 나은 소스 코드를 작성하였다.



삼각형 출력 예제 2
/*
 *  Filename: printTriangle2.go
 *            Print a triangle on console.
 *
 *  Execute: go run printTriangle2.go
 * 
 *  or
 * 
 *  Compile: go build printTriangle2.go
 *  Execute: ./printTriangle2
 *
 *      Date:  2012/06/26
 *    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]
 */

package main

import (
    "fmt"
)

func println(s string) {
    fmt.Printf("%s\n", s)
}

func print(s string) {
    fmt.Printf("%s", s)
}

func printTriange() {
    var i, k int
    for i = 0; i < 8; i++ {
        for k = 0;  k < 8 - i; k++ {
            print(" ")
        }
        for k = 0;  k < 2*i + 1; k++ {
            if k == 0 || k == 2*i {
                print("*");
            } else {
                print(" ")
            }
        }
        for k = 0;  k < 8 - i; k++ {
            print(" ")
        }
        println("")
    }
    for i = 0; i < 17; i++ {
        print("*")
    }
    println("")
}

func main() {
    printTriange()
}



위의 소스 코드는 컨솔 출력 함수 fmt.Printf() 와  for 구문을 적절히 사용하여 구현되었다. 숫자 몇 곳만 수정하면 출력되는 삼각형의 크기를 바꿀 수 있다. 한 줄에 출력될 문자를 구성하는 알고리즘은 위의 예제와 근본적으로 같지만 string 타입의 변수를 적절히 사용하여 한 즐씩 출력하는 소스 코드를 다음 예제와 같이 작성해 보았다.



삼각형 출력 예제 3
/*
 *  Filename: printTriangle3.go
 *            Print a triangle on console.
 *
 *  Execute: go run printTriangle3.go
 * 
 *  or
 * 
 *  Compile: go build printTriangle3.go
 *  Execute: ./printTriangle3
 *
 *      Date:  2012/06/26
 *    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]
 */

package main

import (
    "fmt"
)

func println(s string) {
    fmt.Printf("%s\n", s)
}

func print(s string) {
    fmt.Printf("%s", s)
}

func printTriange() {
    var line string = "                 "
    var pline2 string
    var i int
    pline2 = line[0:8] + "*"
    println(pline2)
    for i = 1; i < 8; i++ {
        pline2 = line[0:8-i] + "*" + line[8-i+1:8]
        pline2 += line[0:i] + "*"
        println(pline2)
    }

    pline2 = ""
    for i = 0; i < 17; i++ {
        pline2 += "*"
    }
    println(pline2)
}

func main() {
    printTriange()




별(*) 문자를 이용하여 삼각형을 출력하는 일은 빈칸  문자와 별 문자를 적당한 좌표(위치)에 촐력하는 일이다. 기본 문자열을 빈칸 만으로로 구성된 문자열로 하고, 이 문자열에 한 두 개의 빈칸을 별(*) 문자로 바꾸어 출력하는 기법으로 작성한 것이 다음 소스 코드이다. 단, 마지막 줄에 츨력될 문자열은 stars라는 별도의 변수로 처리하였다.



삼각형 출력 예제 4
/*
 *  Filename: printTriangle4.go
 *            Print a triangle on console.
 *
 *  Execute: go run printTriangle4.go
 * 
 *  or
 * 
 *  Compile: go build printTriangle4.go
 *  Execute: ./printTriangle4
 *
 *      Date:  2012/06/26
 *    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]
 */

package main

import (
    "fmt"
)

func println(s string) {
    fmt.Printf("%s\n", s)
}

func print(s string) {
    fmt.Printf("%s", s)
}

func printTriange() {
    var whites string = "                 ";
    var stars string  = "*****************";
    var line2 string
    var i int
    println(whites[0:8]+"*"+whites[9:]);
    for i = 1; i < 8; i++ {
        line2 = whites;
        println(line2[:8-i] + "*"  + line2[8-i+1:8+i] + "*" + line2[8+i+1:]);
    }
    println(stars);
}

func main() {
    printTriange()
}




빈칸 문자를 별(*) 문자로 바꾸기 위해, 위의 소스 코드에서는 line2 변수에 whites 의 스트링 값을 통째로 저장하여 출력하였지만, 다음 소스 코드에서는 line2 변수에 출력될 스트링값을 만든어 저장한 후 line2를 출력하였다.



삼각형 출력 예제 5
/*
 *  Filename: printTriangle5.go
 *            Print a triangle on console.
 *
 *  Execute: go run printTriangle5.go
 * 
 *  or
 * 
 *  Compile: go build printTriangle5.go
 *  Execute: ./printTriangle5
 *
 *      Date:  2012/06/26
 *    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]
 */

package main

import (
    "fmt"
)

func println(s string) {
    fmt.Printf("%s\n", s)
}

func print(s string) {
    fmt.Printf("%s", s)
}

func printTriange() {
    var whites string = "                 ";
    var stars string  = "*****************";
    var line2 string
    var i int
    var start int = 8
    line2 = whites[0:start]+"*"+whites[start+1:]
    println(line2);
    for i = 1; i < 8; i++ {
        line2 = whites[0:start-i]+"*"+whites[start-i+1:start+i] + "*" + line2[start+i+1:]
        println(line2)
    }
    println(stars)
}

func main() {
    printTriange()
}




출력되는 삼각형이 좌우 대칭이라는 사실에 착안하여, 다음 소스 코드에서는 각 줄을 처음 8자, 중앙 한 문자, 끝 8자(처음 8자의 역순)로 string을 만들어 출력하였다.



삼각형 출력 예제 6
/*
 *  Filename: printTriangle6.go
 *            Print a triangle on console.
 *
 *  Execute: go run printTriangle6.go
 * 
 *  or
 * 
 *  Compile: go build printTriangle6.go
 *  Execute: ./printTriangle6
 *
 *      Date:  2012/06/26
 *    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]
 */

package main

import (
    "fmt"
)

func println(s string) {
    fmt.Printf("%s\n", s)
}

func print(s string) {
    fmt.Printf("%s", s)
}

func reverse(s string) string {
    var n int = len(s)
    var t string = ""
    var i int
    for i = 0; i < n; i++ {
        t += s[n - i - 1:n-i]
    }
    return t
}

func printTriange() {
    var whites string = "        "
    var stars string  = "********"
    var line2 string
    var i int
    var start int = 8
    line2 = whites + "*" + whites
    println(line2);
    for i = 1; i < 8; i++ {
        line2 = whites[0:start-i]+"*"+whites[start-i+1:]
        print(line2)
        line2 = reverse(line2)
        println(" " + line2)
    }
    println(stars + "*" + stars)
}

func main() {
    printTriange()
}




다음 소스 코드는 한 줄에 출력될 문자열의 데이터를 17비트 이진법 수로 구성하고, 이 이진법수의 비트가 0인 곳에는 빈칸을, 1인 곳에는 별(*)을 출력하는 기법으로 작성되었다.



삼각형 출력 예제 7
/*
 *  Filename: printTriangle7.go
 *            Print a triangle on console.
 *
 *  Execute: go run printTriangle7.go
 * 
 *  or
 * 
 *  Compile: go build printTriangle7.go
 *  Execute: ./printTriangle7
 *
 *      Date:  2012/06/26
 *    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]
 */

package main

import (
    "fmt"
)

func println(s string) {
    fmt.Printf("%s\n", s)
}

func print(s string) {
    fmt.Printf("%s", s)
}

func convertItoA(num int64, radix int) (ret string) {
    var BASE36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    var tmp string
    var arr string
    var q, r int64
    var i, n int
    var isNegative bool = false

    if num < 0 {
        isNegative = true
        num = -num
    }
    arr = ""
    q = num
    r = 0

    for q >= int64(radix) {
        r = q % int64(radix)
        q = q / int64(radix)
        tmp = BASE36[r:r+1]
        arr += tmp
    }
    tmp = BASE36[q:q+1]
    arr += tmp
    if isNegative {
        arr += "-"
    }

    n = len(arr)
    for i = 0; i < n; i++ {
        ret += arr[n - i - 1:n-i]
    }
    return ret
}

func convertAtoI(str string, radix int) (ret int64) {
    var isNegative bool = false
    var len int = len(str)
    var c byte
    var i int
    var val int64

    c = str[0]
    if c == '-' {
        isNegative = true
    } else if c >= '0' && c <= '9' {
        ret = int64(c) - int64('0');
    } else if c >= 'A' && c <= 'Z' {
        ret = int64(c - 'A') + 10
    } else if c >= 'a' && c <= 'z' {
        ret = int64(c - 'a') + 10;
    }
    if ret >= int64(radix) {
        println("        Invalid character!")
        return ret
    }

    for i = 1; i < len; i++ {
        c = str[i]
        ret *= int64(radix)
        if c >= '0' && c <= '9' {
            val = int64(c - '0')
        } else if c >= 'A' && c <= 'Z' {
            val = int64(c - 'A') + 10
        } else if c >= 'a' && c <= 'z' {
            val = int64(c - 'a') + 10
        }
        if val >= int64(radix) {
            println("        Invalid character!")
            return ret
        }
        ret += val
    }

    if isNegative {
        ret = -ret
    }
   
    return ret
}

func printTriange() {
    var start int = 0x100
    var total int64 = 0
    var val int64 = int64(start)
    var data string
    var i int
    var k uint

    for k = 0; k < 8; k++ {
        val = int64((start << k) | (start >> k))
        data = convertItoA(val, 2);
        for i = 0; i < 17 - len(data); i++ {
            print(" ")
        }
        for i = 0; i < len(data); i++ {
            if data[i] == '0' {
                print(" ")
            } else {
                print("*")
            }
        }
        println("")
        total |= val
    }
    val = int64((start << 8) | (start >> 8))
    total |= val
    data = convertItoA(total, 2)
    for i = 0; i < 17 - len(data); i++ {
        print(" ")
    }
    for i = 0; i < len(data); i++ {
        if data[i] == '0' {
            print(" ")
        } else {
            print("*")
        }
    }
    println("")
}

func main() {
    printTriange()
}




기본적인 원리는 위의 소스 코드와 같지만 이진법수의 한 비트 마다 한 문자씩 츨력하는 대신에 출력된 한 줄의 문자열을 완성하여 이를 fmt.Printf() 로 출력하는 기법으로 재작성한 것이 다음의 소스 코드이다. 자체 구현된 replaceAll() 함수를 이용하여, 모든 0을 빈칸으로, 모든 1을 별(*) 문자로 바꾸었으며, 별(*) 문자만으로 이루어진 마지막 줄 출력을 위해 변수 total을 준비하였다. for 반복 구문의 블럭 내에서 구문

            total |= val

이 하는 일이 무엇인지 이해할 수 있으면 좋다.




삼각형 출력 예제 8
/*
 *  Filename: printTriangle8.go
 *            Print a triangle on console.
 *
 *  Execute: go run printTriangle8.go
 * 
 *  or
 * 
 *  Compile: go build printTriangle8.go
 *  Execute: ./printTriangle8
 *
 *      Date:  2012/06/26
 *    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]
 */

package main

import (
    "fmt"
)

func println(s string) {
    fmt.Printf("%s\n", s)
}

func print(s string) {
    fmt.Printf("%s", s)
}

func convertItoA(num int64, radix int) (ret string) {
    var BASE36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    var tmp string
    var arr string
    var q, r int64
    var i, n int
    var isNegative bool = false

    if num < 0 {
        isNegative = true
        num = -num
    }
    arr = ""
    q = num
    r = 0

    for q >= int64(radix) {
        r = q % int64(radix)
        q = q / int64(radix)
        tmp = BASE36[r:r+1]
        arr += tmp
    }
    tmp = BASE36[q:q+1]
    arr += tmp
    if isNegative {
        arr += "-"
    }

    n = len(arr)
    for i = 0; i < n; i++ {
        ret += arr[n - i - 1:n-i]
    }
    return ret
}

func convertAtoI(str string, radix int) (ret int64) {
    var isNegative bool = false
    var len int = len(str)
    var c byte
    var i int
    var val int64

    c = str[0]
    if c == '-' {
        isNegative = true
    } else if c >= '0' && c <= '9' {
        ret = int64(c) - int64('0');
    } else if c >= 'A' && c <= 'Z' {
        ret = int64(c - 'A') + 10
    } else if c >= 'a' && c <= 'z' {
        ret = int64(c - 'a') + 10;
    }
    if ret >= int64(radix) {
        println("        Invalid character!")
        return ret
    }

    for i = 1; i < len; i++ {
        c = str[i]
        ret *= int64(radix)
        if c >= '0' && c <= '9' {
            val = int64(c - '0')
        } else if c >= 'A' && c <= 'Z' {
            val = int64(c - 'A') + 10
        } else if c >= 'a' && c <= 'z' {
            val = int64(c - 'a') + 10
        }
        if val >= int64(radix) {
            println("        Invalid character!")
            return ret
        }
        ret += val
    }

    if isNegative {
        ret = -ret
    }
   
    return ret
}

func replaceAll(str string, s string, t string) string {
    var val string = ""
    var n int = len(s)
    var i int
    for i = 0; i < len(str); i++ {
        if i + n <= len(str) && str[i:i+n] == s {
            val += t
            i += n -1
        } else {
            val += str[i:i+1]
        }
    }
    return val
}

func printTriange() {
    var zeros string = "00000000"
    var start int = 0x100
    var total int64 = 0
    var val int64 = int64(start)
    var data string
    var line string
    var k uint

    for k = 0; k < 8; k++ {
        val = int64((start << k) | (start >> k))
        data = convertItoA(val, 2)
        line = zeros[0: 17 - len(data)]
        line += data
        line = replaceAll(line, "0", " ")
        line = replaceAll(line, "1", "*")
        println(line)
        total |= val
    }

    val = int64((start << 8) | (start >> 8))
    total |= val;
    line = convertItoA(total, 2)
    line = replaceAll(line, "0", " ")
    line = replaceAll(line, "1", "*")
    println(line)
}

func main() {
    printTriange()
}




심각형의 좌우 대칭성과 []byte 타입의 자료를 이용하여 한 줄씩 출력하는 기법이다.
별(*) 문자만으로 이루어진 마지막 줄 출력을 위해 변수 last를 준비하였다.



삼각형 출력 예제 9
/*
 *  Filename: printTriangle9.go
 *            Print a triangle on console.
 *
 *  Execute: go run printTriangle9.go
 * 
 *  or
 * 
 *  Compile: go build printTriangle9.go
 *  Execute: ./printTriangle9
 *
 *      Date:  2012/06/27
 *    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]
 */

package main

import (
    "fmt"
    "bytes"
)

func println(s string) {
    fmt.Printf("%s\n", s)
}

func print(s string) {
    fmt.Printf("%s", s)
}

func printTriange() {
    var WIDTH int = 17
    var start int = 8
    var data []byte = bytes.Repeat([]byte{' '}, WIDTH)
    var last []byte = bytes.Repeat([]byte{'*'}, WIDTH)
    var i, k int

    for k = 0; k < WIDTH; k++ {
         data[k] = ' '
         last[k] = ' '
    }
    data[start] = '*'
    last[start] = '*'
    for i = 0; i < WIDTH; i++ {
        fmt.Printf("%c", data[i])
    }
    println("")
    data[start] = ' '

    for k = 1; k < WIDTH/2; k++ {
        data[start - k] = '*'
        last[start - k] = '*'
        data[start + k] = '*'
        last[start + k] = '*'

        for i = 0; i < WIDTH; i++ {
            fmt.Printf("%c", data[i])
        }
        println("")
        data[start - k] = ' '
        data[start + k] = ' '
    }

    last[start - WIDTH/2] = '*'
    last[start + WIDTH/2] = '*'
    for i = 0; i < WIDTH; i++ {
        fmt.Printf("%c", last[i])
    }
    println("")
}

func main() {
    printTriange();
}




다음 예제는 수학에서 xy-좌표평면에 점을 찍듯이 논리 구문

             (x + y - 8 == 0) || (y - x + 8 == 0) || (y - 8 == 0)

가 참이 되는 위치에 별(*) 문자를 표시하는 기법으로 작성된 소스 코드이다.




삼각형 출력 예제 10
/*
 *  Filename: printTriangle10.go
 *            Print a triangle on console.
 *
 *  Execute: go run printTriangle10.go
 * 
 *  or
 * 
 *  Compile: go build printTriangle10.go
 *  Execute: ./printTriangle10
 *
 *      Date:  2012/06/27
 *    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]
 */

package main

import (
    "fmt"
)

func println(s string) {
    fmt.Printf("%s\n", s)
}

func print(s string) {
    fmt.Printf("%s", s)
}

func printTriange() {
    var a byte
    var x, y int

    for y = 0; y <= 8; y++ {
        for x = 0; x <= 16; x++ {
            if (x + y - 8 == 0) || (y - x + 8 == 0) || (y - 8 == 0) {
                a = '*';
            } else {
                a = ' ';
            }
            fmt.Printf("%c", a)
        }
        println("")
    }
}

func main() {
    printTriange();
}



Posted by Scripter
,