2012년 4월 30일 월요일

go language




지금은  GO 언어를 들여다 보고있다.

concurrency 는 속도가 목적이 아니라 디자인이 목적이라는 말.
기존 OOP 언어 개발방법에서 class hierachy 를 개발 초기에 결정하는 것이 거의 불가능하다는 점을 GO 는 만져준다.
concurrency + rpc 는 scalability 를 가능하게한다.
개발자의 주 업무는 디자인을 지속적으로 변경하는 일이라는것을 GO 언어는 염두에둔다.
지속적으로 디자인을 변경하는일이 짐이되지 않도록 하는 장치가 들어있는것으로 관찰된다.

GO1 은 아직 완성도가 높지않다.
지원하는 라이브러리가 적다. 특히 UI 가 아쉽다. 부족한 부분을 아직은 SWIG 에 의존하고 있는 상황이다.
빌드속도는 빠른것이 확인되었으나, 실행속도는 아직 확인되지 않았다.
최악의 경우 JAVA 정도의 속도가 될지도 모를일이라고, 나 혼자 걱정하고있다.


GO 에는 함수형 언어의 특성이 들어있다.
GO 에는 class 가 없다.
따라서, GO 는 C++ 개발자에게 디자인 패러다임을 전환할것을 요구한다.

사고의 전환은 개발자들의 시간과 에너지가 필요한일이다.
개발자들이 GO 디자인 패러다임에 적응하는데는 시간이 걸릴것이다.
그러나, 몇년안에 OOP 언어에 지친 개발자들은  GO 언어로 이동할것이다.
몇년안에 지금 부족한 라이브러리가 채워질것이다.


디자인변경이 고통스럽지 않아야한다.
thread, ipc, rpc, shared memory synchronization 기능을 언어차원에서 지원해야한다.
Scalability 가 가능해야한다.
여러가지 OS 에서 빌드할 수 있어야 한다.
실행속도가 빨라야 한다.
이것은 내가 원하는 언어의 요구사항이다.

메인언어로,
GO 언어가 C++ 언어의 대안이 될수 있을것인가.
그것을 앞으로 몇달간 살펴볼 예정이다.

goroutine 과 channel 을 쓸것인가 thread 와 mutex 를 쓸것인가.
그것을 앞으로 몇달간 살펴볼 예정이다.


결국, 나는 Better C++ 을 찾고 있는 중인가.



closure

출처: http://tour.golang.org/#37


모든 함수는 closure 이다.

C++ 문법에 closure 는 없었다.

closure 가 무엇인가.

코드실행결과를 보면, 함수내부의 변수값을 기억한다.

C++ 의 static 변수와 그 기능이 같다.

함수내부변수를 기억하는기능을 가진 함수. 그것이 closure 인가.




package main


import (
"fmt"
)


func main() {


fmt.Println("---------------------")

pos, neg := adder(), adder()
fmt.Printf( "%T\n", pos)

fmt.Println( pos(1))
fmt.Println( pos(1))
fmt.Println( pos(1))
fmt.Println( pos(1))
fmt.Println("---------------------")
fmt.Println( neg(-1))
fmt.Println( neg(-1))
fmt.Println( neg(-1))
fmt.Println( neg(-1))
fmt.Println("---------------------")
}


func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}



D:\DEV\PP\work_4\src>go run hello66.go
---------------------
func(int) int
1
2
3
4
---------------------
-1
-2
-3
-4
---------------------

Functions are values too.


출처: http://tour.golang.org/#36



함수는 값이다.








package main


import (
"fmt"
"math"
)




func main() {

hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}

fmt.Println("---------------------")
fmt.Println( hypot(3, 4))
fmt.Printf( "%T\n", hypot)
fmt.Println("---------------------")


}




D:\DEV\PP\work_4\src>go run hello65.go
---------------------
5
func(float64, float64) float64
---------------------




함수는 값이다. ( Functions are values too. )

런타임에 값의 타입을 정확히 가져올 수 있다는 말은,
Reflection 이 된다는 얘기다.



C++ 로만 말하던 나는,
Java 의 Reflection 이 부러웠다.
Python 의 Reflection 이 부러웠다.



rand

rand

난수는 의미있는 모든것의 근간이 됨을 우리는 알고있다.





package main


import (
"fmt"
"math/rand"
"time"
)




func main() {
seedNum := time.Now().UnixNano()
fmt.Println( "seed: ", seedNum)

rand.Seed( seedNum)

for i := 0; i < 5; i++ {
fmt.Println( rand.Int())
}

}





D:\DEV\PP\work_4\src>go run hello64.go
seed:  1335756904485079200
978468083
42468584
1238955245
1473912459
1318195603



gob

gob.

네트워크상으로 데이터를 전송할때나,
데이터를 파일에 저장할때 엔코딩/디코딩작업이 필요하다.

MFC 에서 Serialize 라는 용어로 표현되었던 그기능.

GO 에서는 gob 이 담당한다.

rpc package 는 gob 을 사용하여 구현되었다고 한다.





package main


import (
"bytes"
"encoding/gob"
"fmt"
"log"
)


type P struct {
X, Y, Z int
Name string
}


type Q struct {
X, Y *int32
Name string
}


func main() {


var network bytes.Buffer
enc := gob.NewEncoder(&network)
dec := gob.NewDecoder(&network)

err := enc.Encode(P{3, 4, 5, "Pythagoras"})
if err != nil {
log.Fatal("encode error:", err)
}

var q Q
err = dec.Decode(&q)
if err != nil {
log.Fatal("decode error:", err)
}

fmt.Printf("%q: {%d, %d}\n", q.Name, *q.X, *q.Y)
}










error

error

error 를 한번 보자.





package main


import "fmt"
import "errors"


func main() {


if _, err := func1( -1); nil != err {
fmt.Println("not good:", err);
fmt.Printf("%T\n", err);
} else {
fmt.Println("good.");
}
}


func func1( f int) ( int, error) {
if f < 0 {
return 0, errors.New("여기서 음수는 에러입니다.")
}
return f, nil
}





D:\DEV\PP\work_4\src>go run hello61.go
not good: 여기서 음수는 에러입니다.
*errors.errorString













2012년 4월 29일 일요일

go chan select



goroutine, channel 과 함께 한 팀을 이루고있는
select 문을 한번 보자.


ch 에 데이터가 들어오기를 기다린다.
그런데, 계속기다리는것이 아니라, 일정시간동안(timeout )만 기다린다.

아래 코드에는 ch 에 데이터를 쓰는 부분이 없지만,
실제 코드 실행중에는 ch 에 데이터가 먼저들어올지,
timeout 이 먼저 발생할지, 알수없는 상황이라고 가정해보자.

사건발생의 임의성을 select 문으로 깔끔하게 묶어냈다.




package main


import "fmt"
import "time"


func main() {


ch := make( chan int)
timeout := make( chan bool, 1)


go func() {
time.Sleep( 1 * time.Second)
timeout <- true
} ()

select {
case <-ch:
fmt.Println("read from ch.")
case <-timeout:
fmt.Println("timeout.")
}


}


코드 출처: http://golang.org/doc/articles/concurrency_patterns.html






panic

panic.

프로그램을 불가피하게 멈춰야할때.
초기화 도중 불시착할때.

panic 은 가능한 안쓰는것이 좋을것이다.





package main


func main() {


func1()


}


func func1( ) {
panic("복구할수없는 에러.")
}







D:\DEV\PP\work_4\src>go run hello59.go
panic: 복구할수없는 에러.


goroutine 1 [running]:
main.func1()
        D:/DEV/PP/work_4/src/hello59.go:13 +0x4a
main.main()
        D:/DEV/PP/work_4/src/hello59.go:8 +0x1b


goroutine 2 [syscall]:
created by runtime.main
        C:/Users/ADMINI~1/AppData/Local/Temp/2/bindist119677522/go/src/pkg/runtime/proc.c:221
exit status 2


D:\DEV\PP\work_4\src>




parallel 은 아직 완전하지 않다.






package main
import "fmt"
import "runtime"
import "os"


func main() {


NCPU := runtime.NumCPU()
fmt.Println(NCPU)

runtime.GOMAXPROCS( NCPU)


maxcpu := os.Getenv("GOMAXPROCS")
fmt.Println( "[", maxcpu, "]")

}






channel


goroutine 이 종료되었을때, 종료보고기능을 위해서 channel 이 필요하다.

channel 은 make 함수를 사용하여 생성한다.

channel 은 통신기능은 물론, 동기화기능까지 제공한다.




나는 "종료보고기능"을 C++, C# 으로 구현해보려 했으나 만족할 만한 답을 찾지 못했다.
개인적인 견해로는, 통신 또는 데이터 공유와 동기화 기능이 하나로 합쳐져야 한다는 생각에는 전적으로 동의한다.
이런 기능이 뼈대로 제공되어야, 응용소프트웨어 분야에서 혁신이 나올 수 있다.






package main
import "fmt"


func main() {

c := make(chan int) // allocate a channel.

go func() {
fmt.Println(  "goroutine 3")
c <- 1 // send a signal; value does not matter
} ()

fmt.Println(  "output 3")

<-c // wait for finish; discard sent value
}






goroutine function literal


goroutine 을 정의할때,
function literal 을 쓰면 편리하다.




package main
import "fmt"
import "time"


func main() {

go func() {
fmt.Println(  "goroutine 2")
} ()

fmt.Println(  "output 2")


time.Sleep( 1000)
}










goroutine

goroutine.


thread 가 아니다. goroutine 이다.


키워드는 go 를 쓴다.






package main
import "fmt"
import "time"


func main() {

go fmt.Println(  "goroutine 1")

fmt.Println(  "output 1")


time.Sleep( 1000)
}














Embedding

Embedding



이것은 아직 이해하지 못했다.
나중에 와서 다시보자.












package main
import "fmt"


func main() {

fmt.Println(  "Embedding <-- subclassing")
}


type Reader interface {
Read( p []byte) (n int, err error)
}


type Writer interface {
Write( p []byte) (n int, err error)
}


type ReadWriter interface {
Reader
Writer
}



형변환 ( Conversion )

형변환 ( Conversion )

GO 언어에도 형변환기능이 있다.





package main
import "fmt"


func main() {

var n1 int = 1
var n2 float64

// error:
//  cannot use n1 (type int) as type float64 in assignment
// n2 = n1


n2 = float64( n1)

fmt.Println(  n2)
}








Interface

Interface

if something can do this, then it can be used here.

만약, Sequence type 이 Len(), Less(i, j int) bool, Swap(i, j int) 기능을 할 수 있다면,
그렇다면 Sequence type 은 sort.Sort() 함수호출에 사용할 수 있다.

Interface 는 어떤기능을 수행하기위한 요구사항, 필요조건을 기술하는 기능이다.

어떤기능을 서술하기위해, C++ 처럼 멤버함수형태로 기술한다면,
유사한 코드를 반복해서 작성해야하거나,
상속체계를 반복해서 수정해야하는 상황이 생길수 밖에 없다.

Interface 는 객체로부터 함수를 분리할 수 있게 해주는 장치로 이해하기로 한다.

함수 이름 앞에 붙어있는 "(s Sequence) funcName" 는
C++ 에서 "Sequence::funcName( Sequence & s)" 정도의 의미로 이해하기로 한다.


Interface 는 GO 언어의 주요한 뼈대에 해당한다는 사실을 느낄 수가 있다.

함수와 함수, 함수와 package, 코드와 프로그램 논리가 Interface 를 통해서 연결되는 것이리라고 예상해볼 수 있다.



package main
import "fmt"
import "sort"


func main() {

a := [...] int { 333, 222, 111 }

var b Sequence = a[0:3]

fmt.Println( "a:", a)
fmt.Println( "b:", b)

sort.Sort(b)

fmt.Println( "b:", b)
}


type Sequence []int


// Methods required by sort.Interface
func (s Sequence) Len() int {
return len(s)
}


func (s Sequence) Less(i, j int) bool {
return s[i] < s[j]
}


func (s Sequence) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}


func (s Sequence) String() string {
str := "["
for i, elem := range s {
if i > 0 {
str += " "
}
str += fmt.Sprint(elem)
}
return str + "]"
}





2012년 4월 28일 토요일

Method, Interface

이것은 아직 이해하지 못했다.
나중에 와서 다시보자.

Method, Interface








package main
import "fmt"


func main() { 

fmt.Println( "test")

var b ByteSlice


//fmt.Fprintf(&b, "%d", 7)
fmt.Fprintf(&b, "a")

fmt.Println( b)

}


type ByteSlice []byte




func (p *ByteSlice) Write( data []byte ) (n int, err error){


slice := *p

slice = data

*p = slice


return len(data), nil
}


func (p * ByteSlice) String() string {
var  str string = fmt.Sprint( *p)
return str
}







init()


소스파일은 자신의 초기화 함수를 정의할 수 있다.

클래스나 객체 단위가 아니다. "소스파일" 단위다.

init 함수는 호출하지 않아도 자동 호출된다.




Finally, each source file can define its own niladic init function






package main
import "fmt"


func main() { 
fmt.Println(  "main 함수 호출됨.")


fmt.Println(  "main 함수 종료됨.")
}


func init() {


fmt.Println(  "init 함수 호출됨.")

}





var

변수.




package main
import "fmt"
import "os"


func main() { 

var (
GOROOT = os.Getenv("GOROOT")
GOPATH = os.Getenv("GOPATH")
)

fmt.Printf( "%T\n", GOROOT)
fmt.Println(  GOROOT)
fmt.Println(  GOPATH)

}


enumerated constants

이것은 아직 이해하지 못했다.
나중에 와서 다시보자.



GO 언어에서 enumerated constants 는 iota enumerator 를 사용하여 만든다.




package main
import "fmt"


func main() { 

type ByteSize float64

const (
_ = iota // ignore first value by assigning to blank identifier
KB ByteSize = 1 << (10 * iota)
MB
GB
TB
)

fmt.Printf( "%T\n", KB)
fmt.Println(  KB)
fmt.Println(  MB)
fmt.Println(  GB)
fmt.Println(  TB)

}



...


... 의 쓰임세 중에 이런것이 있다.

한번 쳐다봐 주자.

append 함수도 한번 쳐다봐 주자.





package main
import "fmt"


func main() { 


a := [...] int { 1, 2, 3}
x := [] int { 1, 2, 3}

fmt.Printf( "type-a: %T\n",  a)
fmt.Printf( "type-x: %T\n",  x)

fmt.Println(  x)

x = append( x, 4, 5, 6)
fmt.Println(  x)

y := [] int { 7, 8, 9 }
x = append( x, y... )
fmt.Println(  x)

}






가변 파라메터

함수에 넘겨주는 parameter 가 여러개일때,
... parameter 를 쓰는 모양새를 쳐다보자.




package main
import "fmt"


func main() { 


fmt.Println(  Min( 41, 32, 23, 14, 75))

}




func Min( a ... int ) int {
min := int( ^uint(0) >> 1) // largest int
for _, i := range a {
if i < min {
min = i
}
}
return min
}




Print format

Print 에서 format

custom type 에대한 format 을 control 하려면,
String() string 함수를 만들면 된다.


%T 는 type of value 를 출력한다.





package main
import "fmt"


func main() { 


var timeZone = map[string] int {
"UTC" : 1,
"EST" : 2,
"CST" : 3,
"MSt" : 4,
"PST" : 5,
}



t := &T{ 7, -2.35, "abc"}
var str string = "abc"

fmt.Printf( "%v \n", t)
fmt.Printf( "%+v \n", t)
fmt.Printf( "%#v \n", t)
fmt.Printf( "%#v \n", timeZone)
fmt.Printf( "%q \n", str)
fmt.Printf( "%#q \n", str)
fmt.Printf( "%x \n", str)
fmt.Printf( "% x \n", str)
fmt.Printf( "%T \n", str)
fmt.Printf( "%T \n", timeZone)

}




type T struct {
a int
b float64
c string
}




func (t *T) String() string {
return fmt.Sprintf( "custom control: %d/%g/%q", t.a, t.b, t.c)
}





Print

Print 에서 format

%d, %x, %v, %+v, %#v





package main
import "fmt"


func main() {    

var x uint64 = 1<<64  - 1
fmt.Printf("%d %x; %d %x\n", x, x, int64(x), int64(x))


var timeZone = map[string] int {
"UTC" : 1,
"EST" : 2,
"CST" : 3,
"MSt" : 4,
"PST" : 5,
}


type T struct {
a int
b float64
c string
}

t := &T{ 7, -2.35, "abc"}

fmt.Printf( "%v   \n", t)
fmt.Printf( "%+v \n", t)
fmt.Printf( "%#v \n", t)
fmt.Printf( "%#v \n", timeZone)

}




print

아.. Print
함수이름이 C 에서 보았던것과 비슷하다.




package main
import "fmt"
import "os"


func main() {    

fmt.Printf( "Hello %d\n", 23)
fmt.Fprint( os.Stdout, "Hello ", 23, "\n")
fmt.Println( "Hello", 23)

var str string = fmt.Sprint("Hello ", 23)
fmt.Println( str)

}





map


map 이 built-in type 이다.

map 을 한번 쳐다보자.





package main
import "fmt"


func main() {    

var timeZone = map[string] int {
"UTC" : 1,
"EST" : 2,
"CST" : 3,
"MSt" : 4,
"PST" : 5,
}

fmt.Println( timeZone)
fmt.Println( timeZone["EST"])

var num int
var ok bool
var tz string
tz = "CST"

num, ok = timeZone[tz]
fmt.Println( num, ok)

// blank identifier
_, ok = timeZone[tz]


tz = "없어요"


// "comma ok" idiom
if num, ok = timeZone[tz]; ok {
fmt.Println( num) 
} else 
{
fmt.Println( "알수없는 time zone:", tz)
}


delete( timeZone, "PST")
fmt.Println( timeZone)

}




array slice passing


배열을 포인터로 넘기는것보다는,
slice 를 쓰는것이 더 좋다.



package main
import "fmt"


func main() {    

array := [...] float64{ 11.0, 21.0, 31.0 }
x := Sum( array[0:3]) 
fmt.Println( x)
}


func Sum( a [] float64) ( sum float64) {
for _, v := range a {
sum += v
}
return
}




array parameter passing


배열을 함수로 넘길때, 포인터로 넘기는것도 가능하다.



package main
import "fmt"


func main() {    

array := [...] float64{ 10.0, 20.0, 30.0 }
x := Sum( &array) // address-of operator
fmt.Println( x)

}


func Sum( a *[3] float64) ( sum float64) {
for _, v := range *a {
sum += v
}
return
}