Dragon Arrow written by Tatsuya Nakaji, all rights reserved animated-dragon-image-0164

go言語 演算子

Oct 17, 2020

Go 演算子


演算子一覧


Expression = UnaryExpr | Expression binary_op Expression .
UnaryExpr  = PrimaryExpr | unary_op UnaryExpr .

binary_op  = "||" | "&&" | rel_op | add_op | mul_op .
rel_op     = "==" | "!=" | "<" | "<=" | ">" | ">=" .
add_op     = "+" | "-" | "|" | "^" .
mul_op     = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .

unary_op   = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .

https://golang.org/ref/spec#Operators


算術演算

演算子

説明

利用例

+,-

符号

+100, -100

+


加算

1+2

-

減算

1-1

/

割り算

4/2

*

掛け算

3*5

%

余り

5%2


package main

func main() {
    println(5 + 2)    // 7 と表示されます
    println(5 - 2)    // 3 と表示されます
    println(5 * 2)    // 10 と表示されます
    println(5 / 2)    // 2 と表示されます
    println(5 % 2)    // 1 と表示されます

    a := 5
    b := 5
    a++
    b--
    println(a)    // 6 と表示されます
    println(b)    // 4 と表示されます
}


代入演算

 

演算子

説明

利用例

=

変数への代入

a = 100

:=

変数の初期化と代入

a := 100

+=,-= など

演算と代入

i += 2

++

i += 1と同義

i++

--

i -= 1と同義

i--


var number int
number = 100    // numberの値は100
number = 200    // numberの値は200


ビット演算


論理積: 二つの命題(PとQとおく)のいずれも真のときに真となり、それ以外のときは偽となる。(P\(\wedge\)Q)

論理回路や2進数の数値による論理積は、二つの入力の両方が1のときのみ出力が1となり、いずれか一方あるいは両方が0の場合は0となる

論理和: 二つの命題(PとQとおく)のいずれか一方あるいは両方が真のときに真となり、いずれも偽のときに偽となるもの。(P\(\vee\)Q)

論理回路や2進数の数値による論理和は、二つの入力のいずか一方あるいは両方が1のとき出力が1となり、いずれも0の場合に0となる

排他的論理和: 2つの入力のどちらか片方が真でもう片方が偽の時には結果が真となり、両方とも真あるいは両方とも偽の時は偽となる演算(論理演算)である。 XOR

否定: 論理回路や2進数の数値による論理和は、真を偽に、偽を真に反転させる。


演算子

説明

利用例

|

論理和

0x10|0x01

&

論理積

0x1&0xf

^

否定


^0x3

^

排他的論理和

0xc^0x3

&^

論理積の否定

0xc&^0x3

<<

左に算術シフト

0x1<<4

>>

右に算術シフト

0x4>>1


package main

import "fmt"

func main() {
    a, b := 255, 85 // 2進数では、11111111, 01010101

    // 論理積
    fmt.Printf("%08b\n", a&b) // 01010101

    // 論理和
    fmt.Printf("%08b\n", a|b) // 11111111

    //排他的論理和
    fmt.Printf("%08b\n", a^b) // 10101010

    // 論理積の否定 
    fmt.Printf("%08b\n", a&^b) // 10101010

    //算術シフト
    c, d := 15, 240            // 00001111, 11110000
    fmt.Printf("%08b\n", c<<4) // 11110000
    fmt.Printf("%08b\n", d>>4) // 00001111
}


論理演算


演算子

説明

利用例

||

または

a || b

&&

かつ

a && b

!

否定

!a


package main

func main() {

    println(5 > 2 || 5 == 2 && 5 < 2)    // true と表示されます
    println(true || false || false)        // true と表示されます
    println(false || false || false)    // false と表示されます
    println(!(5 > 2))    // false と表示されます
    println(!(5 < 2))    // true と表示されます
    println(!(5 > 2 && 5 == 2 && 5 < 2))    // true と表示されます
    println(!(5 > 2 || 5 == 2 || 5 < 2))    // false と表示されます
}


比較演算

演算子

説明

利用例

==

等しいかどうか

a == b

!=

等しくないか

a != b

<

aはbより小さい

a < b

<=

aはb以下

a <= b

>

aはbより大きい

a > b

>=

aはb以上

a >= b



アドレス演算


演算子

説明

利用例

&

ポインタを取得

&a

*

ポインタが指す値を取得

*a


package main

func main() {

    num := 100
    // ポインター名前は、先頭に p を付けたり、最後に ptr を付ける場合が多い。ポインターの型は、ポインターが指し示すアドレスに入っている値の型の前に *(アスタリスク)付けて指定
    var pnum *int = &num
    // pnum := &num という簡略定義もできます
    
    println(pnum)    // 0xc000030780 などと16進数で表示される
    println(*pnum)    // 100 と表示される
    *pnum = 200
    println(pnum)    // 0xc000030780 などと、上記と同じアドレスが表示される
    println(*pnum)    // 200 と表示される
}


参照渡しと値渡し

変数の実体はメモリ上に格納された領域です。

「値渡し」と「参照渡し」とは、関数やメソッドにおける引数の渡し方の種類を表す用語です。説明すると、以下のようになります。

  • 「値渡し (call by value)」とは、コピー元とは異なるメモリー領域に変数の値をコピーする渡し方です。
  • 「参照渡し (call by reference)」とは、変数のメモリ番地を共有するような渡し方です。


参照渡し

違う関数の間で、同じ変数の値を読み書きできたら便利です。その方法の一つとして、グローバル変数を使う方法があります。しかし、プログラミングではグローバル変数の使用は推奨されていません。プログラムの管理が難しくなるからです。そこで使われるのがポインターを使った参照渡しという方法です。

package main

func main() {

    name := "名前は、main です"
    
    println(name)
    someFunc(&name)
    println(name)
}

// ポインタの型には、アスタリスクをつけるのを忘れない、この場合は、関数の引数にはポインタが代入されるので、引数の型にはアスタリスクをつける
func someFunc(name *string) {
    
    *name = "名前は、someFuncで書き換えられました"
    println(*name)
}

(出力)

名前は、main です 

名前は、someFuncで書き換えられました 

名前は、someFuncで書き換えられました


値渡し

値だけを渡してメモリは共有しないので、値を渡した関数でその値を変更しても、コピー元には変更が加わらない

package main

func main() {

    name := "名前は、main です"
    
    println(name)
    someFunc(name)
    println(name)
}

func someFunc(name string) {
    
    name = "名前は、someFunc で書き換えられました"
    println(name)
}

名前は、main です 

名前は、someFunc で書き換えられました 

名前は、main です


チャネル演算

goroutineとchannelの2つを理解する必要がある


goroutine (ゴルーチン): Go ランタイムによって管理される並行処理を扱うためのコルーチン。 普通のコルーチンとは違って処理の割り込みや再開を開発者がプログラム上から制御することはできない。

ゴルーチンはM:Nモデルと呼ばれる複数(N)のカーネルスレッドに 複数(M)のユーザスレッドを対応させたものにスケジューリングされるため、 複数のCPUコアを扱うことができる。

channel (チャネル): 並行実行(同時に実行)されるgoroutine間を接続するパイプ(トンネル)であり、あるゴルーチンがチャネルへ値を送信し、別のゴルーチンがチャネル値を受け取るのに使用する

 

演算子

説明

利用例

<-

チャネルへの送受信

ch<-100, <-ch


func main() {

    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "two"
    }()
    go func() {
        time.Sleep(1 * time.Second)
        c1 <- "one"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1: // チャネル1から送信完了した場合
            fmt.Println("received", msg1)
        case msg2 := <-c2: // チャネル2から送信完了した場合
            fmt.Println("received", msg2)
        }
    }
}

(出力)

received one 

received two