'language'에 해당되는 글 2건

  1. 2016/02/02 글뻥 Go 언어 강좌 2
  2. 2016/01/29 글뻥 Go 언어 강좌 1

Go 언어 강좌 2

Developer/GO 2016/02/02 17:49
이번 편에는 Golang의 Essencial이 될만한 내용만 추려서 예제를 만들어 보았습니다.
먼저 struct 입니다.
C#이나 Java, C/C++ 등에서 자료구조로 많이 쓰이는 자료구조이죠?

이걸 설명하려면, 왜? Struct구조가 필요한지 알고 넘어가야 합니다.

일단 변하지 않는 "상수" const 부터 살펴보죠.
이넘은 연산하거나 변수로 사용할 수 없습니다.
예전에 계산기 두들길때 1+2 = 3 이 나온다 치면 1과 2가 상수입니다. 항상 정해진 값을 가지고 있지요.

package main

import (
    "fmt"
)

func main() {
    const x int = 1
    x += 1
    fmt.Println(x)
}


당연히 위의 소스를 실행하면 다음과 같이 출력됩니다.
D:\DevelopSource\Go Lang>go run testVal.go
# command-line-arguments
.\testVal.go:10: cannot assign to x
처음 한번 할당하면 바꿀수 없습니다. 그러다보니 개발자 입장에서 한번 선언해두고 다시 불러와서 재활용할 수 있는 개념이 필요했는데, 이것이 바로 변수입니다.

package main

import (
    "fmt"
)

func main() {
    var x int = 1
    x += 1
    fmt.Println(x)
}


위의 코드를 실행하면 다음과 같이 변수가 변경된 채로 출력됩니다.
D:\DevelopSource\Go Lang>go run testVal.go
2

하지만, 이것도 한계가 있었습니다. 변수 하나 하나는 재활용 할 수 있지만, 여러개의 숫자를 동시에 넣고 연산하는걸 한방에 하고 싶었습니다.
그래서 만든 개념이 배열입니다.

예를 들어, 한 팀에 5명이 있다고 치고, 5명의 점수를 숫자로 입력하면 한번에 처리하고 싶다면 변수 5개를 쓰는게 아니라, 배열 1번 선언으로 모든걸 해치울 수가 있죠.

package main

import (
    "fmt"
)

func main() {
    var x []int = []int{1, 2, 3, 4, 5}
    x[0] += 1
    fmt.Println(x[0])
}

위의 코드를 실행하면 첫번째 1이 2로 바뀌어 출력됩니다.
D:\DevelopSource\Go Lang>go run testVal.go
2

문제는 여기서 부터입니다.
점수와 이름을 같이 쓰고 싶습니다. 그럼, struct 개념이 없다면 이렇게 사용했겠죠?

package main

import (
    "fmt"
)

func main() {
    var x []string = []string{"a", "b", "c", "e", "f"}
    var y []int = []int{1, 2, 3, 4, 5}
    y[0] += 1
    fmt.Println(x[0], y[0])
}

위의 코드를 실행하면 각 배열의 첫번째 값만 출력이 됩니다.
D:\DevelopSource\Go Lang>go run testVal.go
a 2
하지만, 역시 개발자는 줄이고 싶어합니다.
그래서, 만든 개념이 struct 입니다.
다양한 변수 Type를 한번에 저장할 수 있지요.
package main

import (
    "fmt"
)

func main() {
    s := Scoreboard{"a", 1}
    s.y += 1
    fmt.Println(s.x, s.y)
}

type Scoreboard struct { //대문자로 쓰면 public입니다. 소문자면 private
    x string
    y int
}


위코드를 실행하면 다음과 같이 출력됩니다.
D:\DevelopSource\Go Lang>go run testVal.go
a 2
짜잔..

여기에, 자주쓰는건 아에 struct method로 넣어버릴 수 있습니다. 
* 다음 예제는 난이도가 위의 예제에 비해 많이 상승해서 Add method 하나만 잘 익혀두면 좋겠네요.

package main

import (
    "fmt"
)

func main() {

    s := Scoreboard{
        []string{"a", "b", "c"},
        []int{2, 3, 4},
    }
    fmt.Println(s.Avg())
    fmt.Println(s.Add(0, 100))
    fmt.Println(s.y[0])
}

type Scoreboard struct {
    x []string
    y []int
}

func (s Scoreboard) Add(num int, sco int) int {
    s.y[num] += sco
    return s.y[num]
}

func (s Scoreboard) Avg() float32 {
    var _sum int = 0
    for i := 0; i < len(s.y); i++ {
        _sum += s.y[i]
    }
    var _av float32 = float32(_sum / len(s.y))
    return _av
}


위의 코드를 실행하면, 먼저 입력된 점수의 평균인 3을 출력하고 첫번째 학생에게 100점을 주고, 제대로 들어갔는지 확인합니다.
D:\DevelopSource\Go Lang>go run testVal.go
3
102
102
하지만, 이역시 문제가 많습니다.
왜냐하면, 

Scoreboard{
        []string{"a", "b", "c"},
        []int{2, 3, 4},
    }

이렇게 선언해놓고는 데이터가 추가되면 어떻게 될까요? 혹은 이름은 10자리인데, 점수는 11자리.. 
일렇게 되면 망했죠... 망했어요...

그래서 C#이나 Java 같은 현대적인 언어들은 List로 이를 해결합니다.
Golang도 List Package가 있지만, Go 언어의 기본 패키지로 이를 해결하려면 결국 포인트를 쓰던가... 아니면 처음부터 Slice로 만듭니다.

package main

import (
    "fmt"
)

func main() {

    //아래는 좀 원시적인 방법으로 포인터 선언하고 새로 다 때려넣는 방법
    //s := []*Score{}
    //row := new(Score)
    //row.x = "a"
    //row.y = 1
    //s = append(s, row)

    //조금 아는척하면서 Slice로 init하고 Slice append로 데이터 넣는 방법
    Scoreboard := Scoreboard{
        {x: "a", y: 1},
        {x: "b", y: 2},
        {x: "c", y: 3},
    }
    Scoreboard = append(Scoreboard, Score{x: "d", y: 4})
    fmt.Println(Scoreboard[3].x, Scoreboard[3].y)
}

type Score struct {
    x string
    y int
}

type Scoreboard []Score


위 코드를 실행하면 
D:\DevelopSource\Go Lang>go run testVal.go
d 4
가 출력됩니다. =)

이제 마지막으로 한 맺힌 Tree 만들어 봅시다.

A는 B, C를 가집니다. B는 D, E를 가지고 C는 F를 가지고 있습니다. B를 삭제하면 이하에 있는 값이 삭제되도록 하는 겁니다.
* 단 빈공간으로 입력했던 흔적이 남아야 합니다.
package main

import (
    "fmt"
)

func main() {
    ScoreTree := ScoreTree{
         {Parent: "", Value: "A"},
          {Parent: "A", Value: "B"},
          {Parent: "A", Value: "C"},
          {Parent: "B", Value: "D"},
          {Parent: "B", Value: "E"},
          {Parent: "C", Value: "F"},
    }

     Remove(ScoreTree, "B")

     for key, val := range ScoreTree {
         fmt.Println("K:", key, "P:", val.Parent, "V:", val.Value)
     }
}

func Remove(obj ScoreTree, _x string) {
     fmt.Println("Remove Letter:", _x)

     for key, val := range obj {
         if val.Value == _x {
             obj[key].Value = ""
         }
         if val.Parent == _x {
             obj[key].Parent = ""
             Remove(obj, obj[key].Value)
         }
     }
}

type Score struct {
     Parent string
     Value string
}

type ScoreTree []Score


위의 코드를 실행하면...
D:\DevelopSource\Go Lang>go run testVal.go
Remove Letter: B
Remove Letter: D
Remove Letter: E
K: 0 P: V: A
K: 1 P: A V:
K: 2 P: A V: C
K: 3 P: V:
K: 4 P: V:
K: 5 P: C V: F

깔끔하게 실행되는 군요! 

한이 서린 재귀함수 호출은 이제 마지막 단계로 빈 값이 있는 Item들을 날려버리는 겁니다.

package main

import (
    "fmt"
)

func main() {
     ScoreTree := ScoreTree{
         {Parent: "", Value: "A"},
         {Parent: "A", Value: "B"},
         {Parent: "A", Value: "C"},
         {Parent: "B", Value: "D"},
         {Parent: "B", Value: "E"},
         {Parent: "C", Value: "F"},
     }

     ScoreTree.Remove(ScoreTree, "B")
     ScoreTree.Trunc()
     for key, val := range ScoreTree {
         fmt.Println("K:", key, "P:", val.Parent, "V:", val.Value)
     }
}

func (s ScoreTree) Remove(obj ScoreTree, _x string) {
     for key, val := range obj {
         if val.Value == _x {
             obj[key].Value = ""
         }
         if val.Parent == _x {
             obj[key].Parent = ""
             s.Remove(obj, obj[key].Value)
         }
     }
}

func (s *ScoreTree) Trunc() {
     var r ScoreTree
     for _, val := range *s {
         if val.Value != "" {
             r = append(r, val)
         }
    }
     *s = r
}

type Score struct {
     Parent string
     Value string
}

type ScoreTree []Score

실행하면... 깔끔하게 새로 작성해 줍니다. (포인터 드뎌 한번 썼네요...)
D:\DevelopSource\Go Lang>go run testVal.go
K: 0 P: V: A
K: 1 P: A V: C
K: 2 P: C V: F

이번편 마지막으로 드디어 Interface입니다.
OOP에서는 대표적으로 abstract Class와 interface Class가 있습니다.
하지만, 추상클래스의 경우는 처음 설계에서 대충 껍데기만 만들고 들어가서는 상속받아 사용하고 이걸 또 상속받아 사용하고 또 상속받아 사용하고... 이러다 망하죠.
그래서 최근 추세는 interface class를 만들어 놓고 1단계만 확장하는 개념으로 사용합니다.
여기에 C#은 Vrtual method라는 개념까지 겹쳐있어서 머리아프게 하지만, 별거 없음.

public class 짐승
{
 public virtual void 짖기()
 {
 Console.WriteLine("없어");
 }
}
 
public class 강아지 : 짐승
{
 public override void 짖기()
 {
 Console.WriteLine("으르릉!");
 }
}
 
//이하 사용법
//강아지 강쥐 = new 강아지();
//강쥐.짖기();


이하 추상화 클래스 C#
public abstract class 짐승
{
 public abstract void 짖기();
}
 
public class 강아지 : 짐승
{
 public override void 짖기()
 {
 Console.WriteLine("멍멍멍멍");
 }
}
 
//사용
//강아지 강쥐 = new 강아지();
//강쥐.짖기();


이하는 interface입니다.
 public interface 짐승
 {
 void 짖기();
 }

 class 강아지 : 짐승
 {
 private string _이름;
 public void 짖기()
 {
 Console.WriteLine(이름 + ":멍멍!");
 }
 public string 이름
 {
 get
 {
 return _이름;
 }
 set
 {
_이름 = value;
 }
 }
 }

//사용
//강아지 강쥐 = new 강아지();
//강쥐.이름 = "멍멍이";
//강쥐.짖기(); 


뭐.. 둘이 사실상 비슷합니다만, 메소드를 오버라이딩 하지 않아도 되기 때문에 인터페이스가 약간 유리? 그리고, 무한 상속 루프에 안걸리죠.
그래서인지 Golang은 인터페이스만 지원합니다.

뭐 암튼... 인터페이스 예제는 다음과 같습니다.

package main

import (
    "fmt"
)

func main() {
     fmt.Println("=============")
     maru := Dog{Animal{"Maru", 5, 3, 1}, "LA"}
     var m Dogs = maru
     m.Hi()
     m.Stay()
     fmt.Println("=============")
     ruru := Cat{Animal{"Ruru", 2, 5, 10}, "Seoul"}
     var c Cats = ruru
     c.Hi()
}

type Animal struct {
     name string
     age int
     speed int
     attack int
}

func (a Animal) Hi() {
     fmt.Println("Im Animal as ", a.name)
}

type Dog struct {
     Animal
     home string
}

func (d Dog) Stay() {
     fmt.Println("I stay at ", d.home)
}

type Cat struct {
     Animal
     home string
}

func (c Cat) Hi() {
     fmt.Println("Ningan! Give me food!")
}

type Dogs interface {
     Hi()
     Stay()
}

type Cats interface {
     Hi()
}


위코드는
- Dogs : Animal struct method중 Hi()를 그냥사용하고 Stay() 추가했습니다.
- Cats : Animal struct method중 Hi()를 교체했습니다.

실행결과는 
D:\DevelopSource\Go Lang>go run testVal.go
=============
Im Animal as Maru
I stay at LA
=============
Ningan! Give me food!

아마도, Go Language의 수 많은 부분중에 기본 사용법 80% 이상은 예제로 남긴것 같네요.
그럼 다음 시간에는 웹서버 그냥 한번 띄워보겠습니다.

* 유니티 플러그인을 이걸로 대체할 수 있으면 좋겠는데 말입니다. LOL
(찾아본 결과 Go로 생성되는 .lib는 C로 Wrapper만들어서 사용할 수 있다는 이야기가 있네요...)

* 위에서 포인터에 대한 설명없이 포인터를 사용했는데, 다음의 예제로 감잡아서 그냥 사용했어요.

=================
package main

import (
    "fmt"
)

func main() {
    a := 43 //var a int = 43;
    fmt.Println("Value of a : ", a)
    fmt.Println("Reference of a : ", &a)

    b := &a //var b *int = &a;
    fmt.Println("Address of a : ", b)
    fmt.Println("Value of a : ", *b)
    fmt.Println("Reference of a : ", &b)

    *b = 32
    fmt.Println("Value of a by *b handle : ", a)
}


결과는 
D:\DevelopSource\Go Lang>go run Pointer.go
Value of a : 43
Reference of a : 0xc0820022b0
Address of a : 0xc0820022b0
Value of a : 43
Reference of a : 0xc082024028
Value of a by *b handle : 32

포인터는 그냥 주소만 가지고 있고 실재 값은 다른곳에 저장되는 이른바 "바로 가기" 폴더입니다.
해당 값을 꺼내올때는 & (레퍼런스)로 꺼내 올 수 있구요.

2016/02/02 17:49 2016/02/02 17:49

Go 언어 강좌 1

Developer/GO 2016/01/29 18:46

미국와서 취직도 안되고, (사실 지원도 1월 이후로 안하고 있어요 ㅋ)
하던차에 Google 느님들이 만드신 Go 언어를 독학하고 있는데...
이 언어 대박입니다!

제가 대박이라 하는 이유는...
- 자바와 같은 멀티 OS기반입니다.
- 코드가 간결합니다.
- C같은 Native compile이 됩니다.
- 별도 VM을 돌리지 않습니다.
- Node.js 나 PHP 처럼 웹서버에 탑재되는게 아니라, 웹서버를 만들고 컨텐츠를 작성할 수 있습니다.
- C와 연동이 됩니다.
- iOS / Andoroid 다됩니다. 참고 : http://www.sitepoint.com/ios-and-andro ··· th-go%2F
- library도!!! https://talks.golang.org/2015/gophercon-go-on-mobile.slide#1


이정도면 배워둘만 하지 않을까요?

앞으로 많은 회사들이 Go로 서버 만들거 같은 느낌이 드네요.
그래서, Unity3D와 Go로 서버만드는 걸 목표로 공부하면서, 과정을 남기려 합니다.

1. 설치
저는 윈도우와 OSX를 사랑하기 땜시, 그냥 윈도우에서 실행할 수 있는 Go를 다운로드 받았습니다.
공식사이트 : https://golang.org/
다운로드 : https://golang.org/dl/

윈도우는 다운로드 후 실행하면 끝!

2. 설치확인
- 윈도우 10기준으로 좌측하단 "웹 및 Windows 검색" 입력창에 cmd 입력, 명령 프롬프트 실행
- 그 이하에서는 직접실행 창에 cmd.exe 입력후 엔터
- 시커먼 명령 프롬프트 창이 열리면 "go"라고 입력해서 실행되는지 확인

* 실행결과

사용자 삽입 이미지

3. 에디터 선정
Visual Studio에서도 plugin을 설치하면 "Go"언어 지원을 받을 수 있지만,
저는 Notepad++을 사랑하기 때문에 이걸 기준으로 설명합니다.

다른 에디터 지원 목록 : https://github.com/golang/go/wiki/IDEsAndTextEditorPlugins
Visual Studio 사용자는 여기로 : https://marketplace.visualstudio.com/items?itemName=lukehoban.Go

- Notepad++ : https://notepad-plus-plus.org/ 에서 다운로드
다운로드후 실행하면 설치 끝.

이제 Notepad++가 "Go"를 인식할 수 있도록 Plugin을 설치할 차례입니다.
(그냥 해도 되지만, Syntax highlight와 Function, Auto Complete 기능은 기억력 감퇴중인 늙은 개발자에게 필수죠)

- Plugin : https://github.com/chai2010/notepadplus-go

다운로드 후에 압축해제하고
- userDefineLang.xml 파일을 "%AppData%\Notepad++" 폴더에 복사
- 먼저 "%AppData%\Notepad++\functionList.xml"파일을 열어두고, 압축파일에 있는 "functionList.xml"파일을 열어서 해당 XML코드를 복사해서 아까 열어둔 원본 functionList.xml 파일 안에 <parsers>~</parsers> 안에 붙여 넣기
- 압축파일에 있는 go.xml 파일을  "{Notepad++에 설치경로}\plugins\APIs"에 복사
- userDefineLang.xml 파일을 열어서 "<NotepadPlus>"와 "</NotepadPlus>" 테그 부분에 주석 처리 (<!-- //-->) 된 부분을 주석 해제후 저장
- Notepad++이 실행중이라면 종료후 재실행

4. Hello World 작성
Notepad++에서 다음과 같이 코드 작성
package main
 
import "fmt"
 
func main() {
    fmt.Printf("hello, world")
}


* 산뜻하게 Syntax highlight 까지 적용
사용자 삽입 이미지

확장자는 ".go"로 임의 폴더에 저장

5. 실행
해당 소스가 있는 폴더로 이동후

go run helloWorld.go 실행
사용자 삽입 이미지

6. Native 빌드
위의 경로에서
go build helloWorld.go 실행후 helloWorld.exe 실행
사용자 삽입 이미지


7. 한글코딩해보기
모국어로 코딩할 수 있는 환경을 사랑합니다! (C# 빠가 된 이유중 하나입죠...)
현대 언어니까 당연히 UTF-8로 코딩해봐야 하지 않을까요?

package main
 
import "fmt"
 
func main() {
    초기화();
}

func 초기화() {
    fmt.Printf("안녕하세요?");
}


근데 Notepad++로 작업하면 이렇게 한글이 작게나와 불편합니다.

사용자 삽입 이미지
이건 Notepad++의 상단 메뉴 "설정"에서 "스타일" 클릭하시고...
다음 메뉴처럼 변경하면 간단히 해결됩니다.
사용자 삽입 이미지
* 짜잔~!
사용자 삽입 이미지
이제 아까와 같이 실행 해봅시다.
사용자 삽입 이미지
이 아이는 착한 아이입니다. 설마 Native compile 안될까봐 해봤습니다.
사용자 삽입 이미지

8. 함수 확장해보기
한글 함수가 되는걸 확인했으니 이제 함수형태를 확장 해보겠습니다.

package main
 
import "fmt"
 
func main() {
    인사하기(5); 
}

func 인사하기(_x int) {
    if _x%2 == 0 {
        fmt.Printf("안녕하세요?");
    } else {
        fmt.Printf("아님 말고");
    }
    if _x > 0  { 
        인사하기(하나씩까기(_x));
    } 
}

func 하나씩까기(_x int) int {
    _x = _x - 1 ;
    return _x;
}


흔하디 흔한 재귀함수입니다.
(영어로 재귀함수를 뭐라 부르는지 몰라서 인터뷰때 "재귀함수 적용할까?" 물어 보지 못한게 한이 되어 앞으로 꾸준히 사용할겁니다. 엉엉 T_T)

사용자 삽입 이미지
여기서 신기한건
- fmt.Printf 와 같이 메소드명 첫글자가 대문자이면 Public 타입 (외부 엑세스 가능)
(한글 코딩하면 규칙에 위배되니 여기까지만 한글 코딩할께요.)
- fmt.Printf 대신 fmt.Println 사용하면 행 자동 바꿈.
- 타입을 항상 뒤에 둔다는 것.
- Function이 다음과 같이도 정의될 수 있다는 것.
func 테스트기능(_x int) (int, int) {
    return 0, 1;
}




다음에는 웹서버 한번 만들어 봅시다.

2016/01/29 18:46 2016/01/29 18:46