LoginSignup
6
4

More than 5 years have passed since last update.

【解答】Go言語初心者向けハンズオン #2

Last updated at Posted at 2018-11-26

※これは解答です。どうしても課題が解けない場合、ご利用ください。

課題1 - 基本問題

package main

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

const (
    PAA   = 1
    GUU   = 2
    CHOKI = 3
)

func main() {
    COUNT := 1000000
    rand.Seed(time.Now().UnixNano())

    result := [3]int{}
    for i := 0; i < COUNT; i++ {
        a := yukpiz()
        b := kent()

        switch {
        case a == b:
            result[0]++
        case a == PAA && b == GUU, a == GUU && b == CHOKI, a == CHOKI && b == PAA:
            result[1]++
        case a == PAA && b == CHOKI, a == GUU && b == PAA, a == CHOKI && b == GUU:
            result[2]++
        default:
            panic("(´・ω・`)")
        }
    }

    fmt.Printf("引分: %d\n", result[0])
    fmt.Printf("勝利: %d\n", result[1])
    fmt.Printf("敗北: %d\n", result[2])
}

func yukpiz() int {
    s := []int{PAA, GUU, CHOKI}
    return s[rand.Intn(len(s))]
}

func kent() int {
    s := []int{PAA, GUU, CHOKI}
    return s[rand.Intn(len(s))]
}

課題1 - 発展問題

※変更箇所のみ記載しています。

func kent() int {
    s := []int{PAA, PAA, PAA, GUU, CHOKI}
    return s[rand.Intn(len(s))]
}

課題2 - 基本問題

package main

import (
    "fmt"
)

type MessageTo int

const (
    MESSAGE_TO_KENT    MessageTo = 0
    MESSAGE_TO_YUKPIZ  MessageTo = 1
    MESSAGE_END_ME     MessageTo = 3
    MESSAGE_END_ME_TOO MessageTo = 4
)

func main() {
    ch := make(chan MessageTo) // 相手にボールを渡す為のチャネル
    done := make(chan bool)    // 全ての会話完了を検知するチャネル

    go kent(ch, done)
    go yukpiz(ch, done)

    // 会話が終わるまでブロック
    <-done

    // 閉じているかを確認
    <-ch
    fmt.Println("会話完了")
}

func kent(ch chan MessageTo, done chan bool) {
    texts := []string{"あなたの名前を教えてください", "私はけんとです", "生ハム食べに行きましょう"}
    for v := range ch {
        switch v {
        case MESSAGE_END_ME_TOO:
            // 終了通知が来たらチャネルを閉じる
            close(ch)
            close(done)
        case MESSAGE_END_ME:
            if len(texts) == 0 {
                // セリフがなくなったら完了
                ch <- MESSAGE_END_ME_TOO
                break
            }
            fallthrough
        case MESSAGE_TO_KENT:
            if len(texts) > 0 {
                fmt.Println("kent:" + texts[0]) // 次のセリフを発言する
                texts = texts[1:]               // 発言済みのセリフを消す
                ch <- MESSAGE_TO_YUKPIZ         // 相手にボールを渡す
            } else {
                ch <- MESSAGE_END_ME
            }
        case MESSAGE_TO_YUKPIZ:
            ch <- MESSAGE_TO_YUKPIZ
        }
    }
}

func yukpiz(ch chan MessageTo, done chan bool) {
    texts := []string{"私はゆくぴずです", "よろしくおねがいします", "行きましょう"}
    ch <- MESSAGE_TO_KENT
    for v := range ch {
        switch v {
        case MESSAGE_END_ME_TOO:
            close(ch)
            close(done)
        case MESSAGE_END_ME:
            if len(texts) == 0 {
                ch <- MESSAGE_END_ME_TOO
                break
            }
            fallthrough
        case MESSAGE_TO_KENT:
            ch <- MESSAGE_TO_KENT
        case MESSAGE_TO_YUKPIZ:
            if len(texts) > 0 {
                fmt.Println("yukpiz:" + texts[0])
                texts = texts[1:]
                ch <- MESSAGE_TO_KENT
            } else {
                ch <- MESSAGE_END_ME
            }
        }
    }
}

課題2 - 発展問題

チャネルは1対1での送信と受信をサポートしていますが、
ブロードキャストのような1対多のような通信をサポートするには工夫が必要です。

package main

import (
    "fmt"
)

type Worker struct {
    Name          string
    BroadcastChan chan int
    DoneChan      chan int
    ReceiveChan   chan int
    Receive       func(*Worker)
}

func main() {
    bch := make(chan int)  //ブロードキャスト用のチャネル
    done := make(chan int) //終了判定用のチャネル

    var workers []*Worker
    workers = append(workers, &Worker{
        Name:          "yukpiz",
        BroadcastChan: bch,
        ReceiveChan:   make(chan int),
        Receive:       yukpiz,
    })
    workers = append(workers, &Worker{
        Name:          "kent",
        BroadcastChan: bch,
        ReceiveChan:   make(chan int),
        Receive:       kent,
    })
    workers = append(workers, &Worker{
        Name:          "ariaki",
        BroadcastChan: bch,
        ReceiveChan:   make(chan int),
        Receive:       ariaki,
    })

    go BroadcastObserve(workers, bch, done)
    for _, w := range workers {
        go w.Receive(w)
    }

    // 全てのチャネルが閉じられている事を確認
    <-done
    <-bch
    for _, w := range workers {
        <-w.ReceiveChan
    }
    fmt.Println("exit!")
}

func BroadcastObserve(workers []*Worker, bch chan int, done chan int) {
    defer close(done)
    for v := range bch {
        for _, w := range workers {
            w.ReceiveChan <- v
        }
    }

    fmt.Println("closed broad cast")
    for _, w := range workers {
        close(w.ReceiveChan)
    }
}

func yukpiz(me *Worker) {
    fmt.Printf("%s: 皆さんの名前を教えてください\n", me.Name)
    me.BroadcastChan <- 1
    for v := range me.ReceiveChan {
        switch v {
        case 3:
            fmt.Printf("%s: ありがとうございます\n", me.Name)
            fmt.Printf("%s: 好きなモノを教えてください\n", me.Name)
            v++
            me.BroadcastChan <- v
        case 5:
            fmt.Printf("%s: 日本酒が好きなんですね\n", me.Name)
            v++
            me.BroadcastChan <- v
        case 7:
            fmt.Printf("%s: 生ハムを食べにいきましょう\n", me.Name)
            v++
            me.BroadcastChan <- v

        }
    }
}

func kent(me *Worker) {
    for v := range me.ReceiveChan {
        switch v {
        case 1:
            fmt.Printf("%s: 私はけんとです\n", me.Name)
            v++
            me.BroadcastChan <- v
        case 6:
            fmt.Printf("%s: 私は生ハムが好きです\n", me.Name)
            v++
            me.BroadcastChan <- v
        case 8:
            fmt.Printf("%s: 行きましょう\n", me.Name)
            close(me.BroadcastChan)
        }
    }
}

func ariaki(me *Worker) {
    for v := range me.ReceiveChan {
        switch v {
        case 2:
            fmt.Printf("%s: 私はありあきです\n", me.Name)
            v++
            me.BroadcastChan <- v
        case 4:
            fmt.Printf("%s: 私は日本酒です\n", me.Name)
            v++
            me.BroadcastChan <- v
        }
    }
}
6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4