search
LoginSignup
4

More than 3 years have passed since last update.

posted at

updated at

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

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

課題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
        }
    }
}

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
What you can do with signing up
4