はじめてのGo言語でランダム会話機を作る

# はじめに
Go言語を使って、自分の発言に対してランダムな接続詞を返してくれる会話機を作ってみます。

**実行イメージ**
![2017-9-3_19-13-55_00-2.png](https://qiita-image-store.s3.amazonaws.com/0/105321/c1536c5c-d998-509b-46fb-287073cd15db.png)



# 開発経緯
自分以外の人と会話している時、不思議と頭の中が整理される感覚があります。
しかし、いざ頭の中を整理したい時に常に誰かと会話できるとは限りません。

そもそも「他人と会話する」とは言うものの、相手が人間である必要はあるのでしょうか。
もしかして、**自分の発言に対してランダムな言葉を返してくれるだけの簡易な機械**があれば、頭の中を整理するのに役立つのではないでしょうか。

つまりこういうことです。
![2017-9-4_4-39-22_00.png](https://qiita-image-store.s3.amazonaws.com/0/105321/0008280b-e623-d73b-e866-dcd875369b47.png)

これを検証するため、最近(?)流行りのGo言語を使ってランダム会話機を作ってみることにしました。

# 開発

ソースコード
https://github.com/MasaoBlue/go-random-joiner

## 1.インストールとHello World
**[はじめての Go 言語 (on Windows)](http://qiita.com/spiegel-im-spiegel/items/dca0df389df1470bdbfa)**

上記を参考にインストール & 動作確認します。
何歳になっても、新しい言語を触るのはわくわくしますね！

## 2.入力受け付け
まずはコンソール入力を受け取る部分を作ります。

```go:sample.go
package main

import "fmt"

func main(){
  fmt.Printf("こんにちは。\n>") // 入力前のプロンプトを表示

  var stdin string // 入力文字列を保存する変数定義
  fmt.Scan(&stdin) // 「&」を付けてstdin変数のポインタを渡し、入力待ち

  // stdin変数に入力文字列が格納されます。
  // fmt.Println(stdin)
}
```

## 3.無限ループで繰り返し入力
入力を繰り返し行うため、無限ループを作ります。

```go:sample.go
// func main
  for { // ループ条件を書かない場合は無限ループになる
    var stdin string
    fmt.Scan(&stdin)
  }
```

## 4.接続詞をファイルから読み込む
入力に対して応答させるための接続詞リストを、テキストファイルから読み込みます。
ファイル入出力の簡単な使い方は**[こちら](http://ashitani.jp/golangtips/tips_file.html#file_Open)**を参照してください。

```go:sample.go
import "os"
import "bufio"

// func main
  // テキスト配列を定義
  // 「:=」で変数宣言と代入を同時に行う
  words := []string{}

  // ファイルを開く
  fp, err := os.Open("words.txt")
  if err != nil {
    panic(err)
  }
  scanner := bufio.NewScanner(fp) // 1行ずつ読み取るためbufio(バッファ付きIO)を使用
  for scanner.Scan() {
    // 1行ずつ読み取ってwords配列に格納
    words = append(words, scanner.Text())
  }
  fp.Close()
```

## 5.接続詞をランダム抽出
読み込んだ接続詞リストから、ランダムに1つを抽出する関数を作ります。
**[こちら](http://ashitani.jp/golangtips/tips_slice.html#slice_Choice)**の実装を元に、intの部分をstringに変更します。

```go:sample.go
import "time"
import "math/rand"

// 配列からランダム抽出
func choice(s []string) string {
    rand.Seed(time.Now().UnixNano()) // 乱数のシードとして現在時刻のナノ秒を渡す
    i := rand.Intn(len(s)) // 0 ～ 配列の要素数までのランダム値取得
    return s[i]
}
```

## 6.結果をファイルに書き込む
せっかく文字列を打ち込むのですから、入力値を保存できるようにしておきましょう。
複数回実行するため、ファイル名には現在時刻を使用します。

日付の文字列変換に関しては**[こちら](http://qiita.com/sotayamashita/items/c8782420391c108e3cac)**にまとまっています。

```go:sample.go
// func main
  // 複数回実行を考慮し、ファイル名に現在時刻を入れる
  const format = "20060102_1504"
  now := time.Now()
  outputFile := "result_" + now.Format(format) + ".txt"

  // 書き込み用ファイルを開く
  fpw, err := os.Create(outputFile)
  if err != nil {
    panic(err)
  }

  w := bufio.NewWriter(fpw)
  for {
    // 入力受け付け
    var stdin string
    fmt.Scan(&stdin)

    // ファイルへ書き込み
    fmt.Fprint(w, stdin + "\n")
    w.Flush()

    // 次の接続詞をランダムで表示
    fmt.Printf(choice(words) + "\n>")
  }

  fpw.Close()
  // ※無限ループをCtrl+cで止めるためこの位置のCloseは実行されない。
  // 　必要な場合は以下の通りrescueを使って対応する。
  //
  // begin
  //   ループ処理
  // rescue Interrupt
  //   fpw.Close()
  // end
```

## 6.実行してみる
以下で実行します。

```
go run sample.go
```

![2017-8-31_23-37-28_00.png](https://qiita-image-store.s3.amazonaws.com/0/105321/d19be120-df55-428c-6098-65a2b2385ab4.png)

ちゃんと動いていそうです。
終了する場合は`Ctrl + c`で停止します。

## 補足：接続詞一覧
必要に応じて以下のページなどからコピーし、`words.txt`に張り付けてください。
**[Yahoo!知恵袋:接続詞の役割＆一覧](https://note.chiebukuro.yahoo.co.jp/detail/n125529)**

# 結論
会話相手がランダムだと、自分の行きたい方向に話が進められずもどかしい。
しかし一方で、**人間と会話するのとは違った会話の広がり**を感じる。

どうしても会話が発散してしまうため、情報を整理する用途には不向きそうです。
逆に、1人でアイデア発想などを行う場合には使えそうな気がします。

# さいごに
This is 初投稿記事です。
「Qiitaに何書けば良いか分からない・・・」という言い訳から脱却するため、思い付きで作ったものを投稿してみました。

きっといつか誰かの役に立つように頑張ろうと思います。

おしまい。