콘솔에 삼각형

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


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

모든 소스 코드에서는 삼각형 출력 부분 담당 함수 printTriange()를 별도로 구현하였다.

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


삼각형 출력 예제 1
--  Filename: printTriangle1.lua
--            Print a triangle on console.
--
--  Execute: lua printTriangle1.lua
--
--      Date:  2008/04/03
--    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]

function printTriange()
    print "        *        "
    print "       * *       "
    print "      *   *      "
    print "     *     *     "
    print "    *       *    "
    print "   *         *   "
    print "  *           *  "
    print " *             * "
    print "*****************"
end

printTriange()




위의 소스 코드는 아무 알고리즘도 없는 너무 단순한 코드이다. 이런 코드를 작성했다간 출력 모양이나 크기를 변경해야 하는 상황을 맞이하면 워드프로세서로 문서 만드는 것 이상으로 많은 수작업을 하거나 아니면 포기하는 지경에 이를 수도 있다. 그래서 다음 처럼 좀 더 나은 소스 코드를 작성하였다. Lua 언어의 출력 함수 print() 는 Python 언어의 print 처럼 새 줄 문자(newline, '\n')를 추가한다. Ruby 언어의 print(), Groovy 언어의 print(), 그리고 Java 언어의 System.out.print(), Python 언어의 sys.std.write() 처럼 새 줄 문자를 추가하지 않는 Lua 함수는 io.write() 이다.




삼각형 출력 예제 2
--  Filename: printTriangle2.lua
--            Print a triangle on console.
--
--  Execute: lua printTriangle2.lua
--
--      Date:  2008/04/03
--    Author:  PH Kim   [ pkim (AT) scripts.pe.kr

function printTriange()
    for i = 0,7 do
        for k = 0,7-i do
            io.write(" ")
        end
        for k = 0,2*i do
            if k == 0 or k == 2*i then
                io.write("*")
     else
                io.write(" ")
            end
        end
        for k = 0,7-i do
            io.write(" ")
        end
        print()
    end

    for i = 1,17 do
        io.write("*")
    end
    print()
end

printTriange()





위의 소스 코드는 Lua의 컨솔 출력 함수 print() 와  for 반복 구문을 적절히 사용하여 구현되었다. 숫자 몇 곳만 수정하면 출력되는 삼각형의 크기를 바꿀 수 있다. 한 줄에 출력될 문자를 구성하는 알고리즘은 위의 예제와 근본적으로 같지만, 그대신 스트링의 테이블(Lua 언어에서는 리스트와 맵을 모두 테이블이러고 부름)을 만들어 한 즐씩 출력하는 소스 코드를 다음 예제와 같이 작성해 보았다.
또 Ruby, Python, Groovy 언어에서 빈칸 17개의 문자로 구성된 리스트를 생성하기 위한 구문

        line2 = [" "]*17

에 해당하는 Lua 코드는

    line2 = {}
    for w in string.gfind((string.rep(" ", 17)), " ") do
        table.insert(line2, w)
    end

이다.



삼각형 출력 예제 3
--  Filename: printTriangle3.lua
--            Print a triangle on console.
--
--  Execute: lua printTriangle3.lua
--
--      Date:  2008/04/03
--    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]

function printTriange()
    local line2 = {}
    for w in string.gfind((string.rep(" ", 17)), " ") do
        table.insert(line2, w)
    end

    for i = 0,7 do
        line2 = {}
        for w in string.gfind((string.rep(" ", 17)), " ") do
            table.insert(line2, w)
        end
        line2[8-i+1] =  '*'
        line2[8+i+1] =  '*'
        print( table.concat(line2) )
    end

    for i = 1,17 do
        line2[i] =  '*'
    end
    print( table.concat(line2) )
end

printTriange()





별(*) 문자를 이용하여 삼각형을 출력하는 일은 빈칸 문자와 별 문자응 적당한 좌표(위치)에 촐력하는 일이다. 출력될 한 줄의 스트링을 완성한 후 하나의 print() 구문으로 출력하는 기법으로 소스 코드를 작성해 보았다. 소스 코드 중에

        local whites = string.rep(" ", 17)
        local stars  = string.rep("*", 17)

은 Ruby, Puthon, Groovy 언어에서 공동으로 쓰이는 코드

        whites = " "*17
        stars = "*"*17

에 대응하는 Lua 코드이다.




삼각형 출력 예제 4
--  Filename: printTriangle4.lua
--            Print a triangle on console.
--
--  Execute: lua printTriangle4.lua
--
--      Date:  2008/04/03
--    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]

function printTriange()
    local whites = string.rep(" ", 17)
    local stars  = string.rep("*", 17)
    local line2 = string.format("%s%s%s", string.sub(whites, 1, 8), "*", string.sub(whites, 1, 8))
    print(line2)
    for i = 1, 7 do
        line2 = string.format("%s%s%s%s%s", string.sub(whites, 1, 8-i), "*", string.sub(whites, 8-i+1,7+i), "*", string.sub(whites, 10+i))
        print(line2)
    end
    print(stars)
end

printTriange()





string은 immutable이라 그 내용을 변경할 수 없지만, 리스트는 그 요소(item)를 아무 때 나 변경할 수 있다. 한줄에 출력될 각 문자를 테이블 타입의 변수 line2에 저장한 후, table.concat() 함수를 이용한 구문

        print(table.concat(line2))

으로 그 리스트의 모든 요소item)가 모두 이어서 출력되게 하였다. 이는 Ruby의

       print(line2,join() + "\n")

또는

        line2.each { |x| print x }
        print("\n")

에 해당하는 Lua 코드이다.





삼각형 출력 예제 5
--  Filename: printTriangle5.lua
--            Print a triangle on console.
--
--  Execute: lua printTriangle5.lua
--
--      Date:  2008/04/03
--    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]

function printTriange()
    local whites = string.rep(" ", 17)
    local stars  = string.rep("*", 17)
    local start = 8
    local line2 = {}
    for w in string.gfind(whites, " ") do
        table.insert(line2, w)
    end
    line2[start + 1] = "*"
    print(table.concat(line2))
    for i = 1,7 do
        line2 = {}
        for w in string.gfind(whites, " ") do
            table.insert(line2, w)
        end
        line2[start - i + 1] = string.sub(stars, start - i + 1, start - i + 1)
        line2[start + i + 1] = string.sub(stars, start + i + 1, start + i + 1)
        print(table.concat(line2))
    end
    print(stars)
end

printTriange()





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



삼각형 출력 예제 6
--  Filename: printTriangle6.lua
--            Print a triangle on console.
--
--  Execute: lua printTriangle6.lua
--
--      Date:  2008/04/03
--    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]

function printTriange()
    local whites = string.rep(" ", 8)
    local stars  = string.rep("*", 8)
    local start = 8
    line = whites .. '*' .. whites
    print(line)
    for i = 2,8 do
        line = string.sub(whites, 1, -i) .. '*' .. string.sub(whites, -i+1, -2)
        print( line .. ' ' .. string.reverse(line) )
    end
    line = stars .. '*' .. stars
    print( line )
end

printTriange()





다음 소스 코드는 한 줄에 출력될 문자열의 데이터를 17비트 이진법 수로 구성하고, 이 이진법수의 비트가 0인 곳에는 빈칸을, 1인 곳에는 별(*)을 출력하는 기법으로 작성되었다. Lua 언어가 구문에서 비트 연산을 지원하지 않는 관계로, 순수 Lua 코드로 작성된 LuaBit 라이브러리를 이용하였다. 압축 파일을 플고 bit.lua 파일을 printTriangle7.lua 가 있는 폴더에 저장하면 다음을 실행시킬 수 있다.




삼각형 출력 예제 7
--  Filename: printTriangle7.lua
--            Print a triangle on console.
--
--  Execute: lua printTriangle7.lua
--
--      Date:  2008/04/03
--    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]

require 'bit'

local BASE36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

function convertItoA(num, radix)
    local isNegative = false
    if num < 0 then
        isNegative = true
        num = -num
    end

    local arr = ""

    local q = num
    local r = 0

    while q >= radix do
        r = math.fmod(q, radix)
        q = math.floor(q / radix)
        arr = arr .. string.sub(BASE36, r+1, r+1)
    end

    arr = arr .. string.sub(BASE36, q+1, q+1)
    if isNegative then
        arr = arr .. "-"
    end

    local n = string.len(arr)
    local ret = ""
    for i = 1, n do
        ret = ret .. string.sub(arr, n - i + 1, n - i + 1)
    end

    return ret
end

function convertAtoI(srcStr, radix)
    local isNegative = false
    local ret = 0
    local len = string.len(srcStr)
    local val = 0
    local c

    c = string.sub(srcStr, 1, 1)
    if c == '-' then
        isNegative = true
    elseif c >= '0' and c <= '9' then
        ret = string.byte(c) - string.byte('0')
    elseif c >= 'A' and c <= 'Z' then
        ret = string.byte(c) - string.byte('A') + 10
    elseif c >= 'a' and c <= 'z' then
        ret = string.byte(c) - string.byte('a') + 10
    end

    if ret >= radix then
        println("        Invalid character!")
        return ret
    end

    for i = 2, len do
        c = string.sub(srcStr, i, i)
        ret = ret * radix
        if c >= '0' and c <= '9' then
            val = string.byte(c) - string.byte('0')
        elseif c >= 'A' and c <= 'Z' then
            val = string.byte(c) - string.byte('A') + 10
        elseif c >= 'a' and c <= 'z' then
            val = string.byte(c) - string.byte('a') + 10
        end

        if val >= radix then
            println("        Invalid character!")
            return ret
        end

        ret = ret + val
    end

    return ret
end

function printTriange()
    local start = 0x100
    local total = 0
    local val = start
    local data = ""
    for k = 0, 7 do
        val = bit.bor(bit.blshift(start, k), bit.brshift(start, k))
        data = convertItoA(val, 2)
        for i = 1, 17-#data do
            io.write(" ")
        end
        for i = 1, #data do
            if string.sub(data, i, i) == "0" then
                io.write(" ")
            else
                io.write("*")
            end
        end
        print()
        total = bit.bor(total, val)
    end

    val = start*math.pow(2,8) + math.floor(start/math.pow(2,8))
    total = bit.bor(total, val)
    data = convertItoA(total, 2)
    for i = 1, 17-#data do
        io.write(" ")
    end
    for i = 1, #data do
        if string.sub(data, i, i) == "0" then
            io.write(" ")
        else
            io.write("*")
        end
    end
    print()
end

printTriange()



위와 마찬가지로 LuaBit 라이브러리를 이용하였다.
기본적인 원리는 위의 소스 코드와 같지만 이진법수의 한 비트 마다 한 문자씩 츨력하는 대신에 출력될 한 줄의 string을 완성하여 이를 print 구문으로 출력하는 기법으로 재작성한 것이 다음의 소스 코드이다. anotherString = string.sub(스트링, 원본, 타겟) 을 이용하여 모든 0을 빈칸으로, 모든 1을 별(*) 문자로 바꾸었으며, 별(*) 문자만으로 이루어진 마지막 줄 출력을 위해 변수 total을 준비하였다. for 반복 구문의 블럭 내에서 구문

            total = bit.bor(total, val)

은 Ruby, Python, Groovy 코드의 구문

            total |= val

에 해당하는데 이 구문이 하는 일이 무엇인지 이해할 수 있으면 좋겠다.





삼각형 출력 예제 8
--  Filename: printTriangle8.lua
--            Print a triangle on console.
--
--  Execute: lua printTriangle8.lua
--
--      Date:  2008/04/03
--    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]

require 'bit'

local BASE36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

function convertItoA(num, radix)
    local isNegative = false
    if num < 0 then
        isNegative = true
        num = -num
    end

    local arr = ""

    local q = num
    local r = 0

    while q >= radix do
        r = math.fmod(q, radix)
        q = math.floor(q / radix)
        arr = arr .. string.sub(BASE36, r+1, r+1)
    end

    arr = arr .. string.sub(BASE36, q+1, q+1)
    if isNegative then
        arr = arr .. "-"
    end

    local n = string.len(arr)
    local ret = ""
    for i = 1, n do
        ret = ret .. string.sub(arr, n - i + 1, n - i + 1)
    end

    return ret
end

function convertAtoI(srcStr, radix)
    local isNegative = false
    local ret = 0
    local len = string.len(srcStr)
    local val = 0
    local c

    c = string.sub(srcStr, 1, 1)
    if c == '-' then
        isNegative = true
    elseif c >= '0' and c <= '9' then
        ret = string.byte(c) - string.byte('0')
    elseif c >= 'A' and c <= 'Z' then
        ret = string.byte(c) - string.byte('A') + 10
    elseif c >= 'a' and c <= 'z' then
        ret = string.byte(c) - string.byte('a') + 10
    end

    if ret >= radix then
        println("        Invalid character!")
        return ret
    end

    for i = 2, len do
        c = string.sub(srcStr, i, i)
        ret = ret * radix
        if c >= '0' and c <= '9' then
            val = string.byte(c) - string.byte('0')
        elseif c >= 'A' and c <= 'Z' then
            val = string.byte(c) - string.byte('A') + 10
        elseif c >= 'a' and c <= 'z' then
            val = string.byte(c) - string.byte('a') + 10
        end

        if val >= radix then
            println("        Invalid character!")
            return ret
        end

        ret = ret + val
    end

    return ret
end

function printTriange()
    local zeros  = "00000000"
    local start = 0x100
    local total = 0
    local val = start
    local line = ""
    local data = ""
    for k = 0, 7 do
        val = bit.bor(bit.blshift(start, k), bit.brshift(start, k))
        data = convertItoA(val, 2)
        line = string.sub(zeros, 1, 17-#data) .. data
        line = string.gsub(line, "0", " ")
        line = string.gsub(line, "1", "*")
        print(line)
        total = bit.bor(total, val)
    end

    val = bit.bor(bit.blshift(start, 8), bit.brshift(start, 8))
    total = bit.bor(total, val)
    data = convertItoA(total, 2)
    line = string.sub(zeros, 1, 17-#data) .. data
    line = string.gsub(line, "0", " ")
    line = string.gsub(line, "1", "*")
    print(line)
end

printTriange()





소스 코드가 처음 것 보다 매우 복잡해졌지만, Lua의 테이블을 이용해서 구현해 보았다. Lua 언어에서 말하는 테이블은 Groovy,  Python 언어에서 말하는 리스트와 맵을 합쳐 놓은 개념이다. Lua의 테이블은 갹채지향 프로그애밍을 할 때도 매우 요긴하게 쓰이는 자료형이다.
아래의 소스 코드에서

        print( table.concat(data) )

은 Ruby 언어에서 작성한 코드

        println data.join

에 해당하는 Lua 코드로서 테이블 안의 모든 요소(item)가 연이어 출력되게 한다.



삼각형 출력 예제 9
--  Filename: printTriangle9.lua
--            Print a triangle on console.
--
--  Execute: lua printTriangle9.lua
--
--      Date:  2008/04/03
--    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]

function printTriange()
    local start = 9
    local data = { }
    local last = { }
    for w in string.gfind(string.rep(" ", 17), " ") do
        table.insert(data, w)
        table.insert(last, w)
    end

    data[start] = "*"
    last[start] = "*"
    print( table.concat(data) )
    data[start] = " "

    for k = 1, 7 do
        data[start - k] = "*"
        last[start - k] = "*"
        data[start + k] = "*"
        last[start + k] = "*"
        print( table.concat(data) )
        data[start - k] = " "
        data[start + k] = " "
    end

    last[start - 8] = "*"
    last[start + 8] = "*"
    print( table.concat(last) )
end

printTriange()






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

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

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




삼각형 출력 예제 10
--  Filename: printTriangle10.lua
--            Print a triangle on console.
--
--  Execute: lua printTriangle10.lua
--
--      Date:  2008/04/03
--    Author:  PH Kim   [ pkim (AT) scripts.pe.kr ]

function printTriange()
    for y = 0, 8 do
        for x = 0, 16 do
            if (x + y - 8 == 0) or (y - x + 8 == 0) or (y - 8 == 0) then
                a = '*'
            else
                a = ' '
            end
            io.write( a )
        end
        print()
    end
end

printTriange()





Creative Commons License

이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.

Posted by Scripter
,