Go言語100本ノックしてみる【第1章】


#100本ノックを始めた理由

- Go言語になれるため
- これまでの「オレオレコード」からリーダブルコードへ矯正のため

2017年6月頃からプログラミングを勉強し、iosアプリ7つ（ゴミ）・Alexaスキル4つ（ゴミ）・Webアプリ1つ（ゴミ）を作成。そろそろ、世のため人のため、OSSに貢献出来るようなプログラマになるため、まずは基礎的なところからやり直そうと思い100本ノックへ。

結構ヒドいコード書いてますが、ありのままをさらけ出そうと思います。



# 00. 文字列の逆順
文字列"stressed"の文字を逆に（末尾から先頭に向かって）並べた文字列を得よ.

```
package main

import (
  "fmt"
)

func reverseStr(str string) string {
  runeStr := []rune(str)
  size := len(runeStr)
  var reverseStr string
  for i := 0; i < size; i++ {
    pos := size - i - 1
    reverseStr += string(runeStr[pos])
  }
  return reverseStr
}

func main() {
  str := "stressed"
  fmt.Println(reverseStr(str))
}
```
forの中がイケてない気がする。
マルチバイトじゃなければrune使わなくていい気もするけど、そもそもruneちゃんと分かってないから、後ほどググります。

#01. 「パタトクカシーー」
「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.

```
package main

import (
  "fmt"
)

func pickOddletter(str string) string {
  runeStr := []rune(str)
  size := len(runeStr)
  var oddStr string
  for i := 0; i < size; i++ {
    if i%2 == 0 {
      oddStr += string(runeStr[i])
    }
  }
  return oddStr
}

func main() {
  str := "パタトクカシー"
  fmt.Println(pickOddletter(str))
}
```
#02. 「パトカー」＋「タクシー」＝「パタトクカシーー」
パトカー」＋「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ．



```
package main

import (
  "fmt"
)

func mixStr(str1, str2 string) string {
  runeStr1 := []rune(str1)
  runeStr2 := []rune(str2)
  size1 := len(runeStr1)
  size2 := len(runeStr2)
  var mixStr string
  for i := 0; i < size1 && i < size2; i++ {
    mixStr += string(runeStr1[i]) + string(runeStr2[i])
  }
  return mixStr
}

func main() {
  str1 := "パトカー"
  str2 := "タクシー"
  fmt.Println(mixStr(str1, str2))
}
```




# 03. 円周率
"Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."という文を単語に分解し，各単語の（アルファベットの）文字数を先頭から出現順に並べたリストを作成せよ．

```
package main

import (
  "fmt"
  "strconv"
  "strings"
)

func separeteBySpace(sentence string) string {
  words := strings.Fields(sentence)
  var wordsLengs string
  for _, v := range words {
    wordsLengs += strconv.Itoa(len(v))
  }
  return wordsLengs
}

func main() {
  sentence := "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
  fmt.Println(separeteBySpace(sentence))
}
```
文字列に変換してるところが、イケてない。


#04. 元素記号
"Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."という文を単語に分解し，1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字，それ以外の単語は先頭に2文字を取り出し，取り出した文字列から単語の位置（先頭から何番目の単語か）への連想配列（辞書型もしくはマップ型）を作成せよ．

```
package main

import (
  "fmt"
  "strings"
)

func periodicTable(sentence string) map[string]int {
  words := strings.Fields(sentence)
  irregular := []int{1, 5, 6, 7, 8, 9, 15, 16, 19} 
  wordMap := make(map[string]int)
  for index, str := range words {
    if contains(irregular, index+1) {
      wordMap[str[0:1]] = index + 1 
    }   
    wordMap[str[0:2]] = index + 1 
  }
  return wordMap

}

func contains(arr []int, number int) bool {
  for _, value := range arr {
    if number == value {
      return true
    }   
  }
  return false
}

func main() {
  sentence := "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
  fmt.Println(periodicTable(sentence))
}
```
1, 5, 6, 7, 8, 9, 15, 16, 19番目だけ1文字取得する処理は、switchで書いた方良かったなと後悔。
あと、他の方々の見ると、スペースで分割する前に、「,」と「.」取り除いてるけど、取り除かなくても結果は変わらない気がするが、あとで勉強します。

#05. n-gram

与えられたシーケンス（文字列やリストなど）からn-gramを作る関数を作成せよ．この関数を用い，"I am an NLPer"という文から単語bi-gram，文字bi-gramを得よ．

```
package main

import (
  "fmt"
  "strings"
)

func ngramByWord(num int, sentence string) [][]string {
  words := strings.Fields(sentence)
  var ngram [][]string
  var tmp []string
  for index, word := range words {
    tmp = append(tmp, word)
    if (index+1)%num == 0 { 
      ngram = append(ngram, tmp)
      tmp = []string{}
    }   
  }
  return ngram
}

func ngramByChar(num int, sentence string) [][]string {
  size := len(sentence)
  var ans [][]string
  for i := 0; i < size; i++ {
    if (i+1)%num == 0 { 
      var tmp []string
      tmp = append(tmp, sentence[i-num+1:i+1])
      ans = append(ans, tmp)
    }   
  }
  return ans 
}
func main() {
  str := "I am an NLPer"
  fmt.Println(ngramByWord(2, str))
  fmt.Println(ngramByChar(2, str))
}
```
恥ずかしながら、n-gramってなんや？って状態だったので、ググりました。ただ、順番にバイグラムだったら、常に前後一文字被りつつ、2文字取り出していくのか、一度取り出した文字は取らずに取り出していくのか不明だったため、とりあえず後者を選択。
ただ、検索とかで使われるっぽいので、おそらく前者でかなと思いつつ。

バイグラム以上になると、破綻するけど、とりあえずGo言語の大枠をつかみたいので、時短のためスルー。（怠惰ですみません）3章ぐらいまで終わったら、もう一回やり直します。

#06. 集合
"paraparaparadise"と"paragraph"に含まれる文字bi-gramの集合を，それぞれ, XとYとして求め，XとYの和集合，積集合，差集合を求めよ．さらに，'se'というbi-gramがXおよびYに含まれるかどうかを調べよ．

```
package main

import (
  "fmt"
)

func ngramByChar(num int, sentence string) []string {
  size := len(sentence)
  var ans []string
  for i := 0; i < size; i++ {
    if (i+1)%num == 0 { 
      ans = append(ans, sentence[i-num+1:i+1])
    }   
  }
  return ans 
}

//重複削除関数
func toUniueSlice(slice []string) []string {
  uniq := []string{}
  tmpMap := make(map[string]bool)
  for _, value := range slice {
    if !tmpMap[value] {
      tmpMap[value] = true
      uniq = append(uniq, value)
    }   
  }
  return uniq
}

//和集合
func union(x, y []string) []string {
  x = append(x, y...)
  union := toUniueSlice(x)
  return union
}

//積集合
func intersection(x, y []string) []string {
  inter := make([]string, 0)
  for _, x_value := range x { 
    for _, y_value := range y { 
      if x_value == y_value {
        inter = append(inter, x_value)
        break
      }   
    }   
  }
  inter = toUniueSlice(inter)
  return inter
}

//差集合
func difference(x, y []string) []string {
  diff := make([]string, 0)
  for _, x_value := range x {
    uniq := true
    for _, y_value := range y {
      if x_value == y_value {
        uniq = false
        break
      }
    }
    if uniq == true {
      diff = append(diff, x_value)
    }
  }
  diff = toUniueSlice(diff)
  return diff
}

func main() {
  str1 := "paraparaparadise"
  str2 := "paragraph"
  x := ngramByChar(2, str1)
  y := ngramByChar(2, str2)
  fmt.Println("X:", x)
  fmt.Println("Y:", y)
  fmt.Println("X∪Y:", union(x, y))
  fmt.Println("X∩Y:", intersection(x, y))
  fmt.Println("X-Y:", difference(x, y))
  fmt.Println("Y-X:", difference(y, x))
}
```
う。集合苦手。
なんか、forぶん回しまくって、よろしくないけど、、。


#07. テンプレートによる文生成
引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ．さらに，x=12, y="気温", z=22.4として，実行結果を確認せよ．


```
package main

import (
  "fmt"
)

func yisZ_atX(x int, y string, z float64) {
  fmt.Printf("%v時の%vは%v", x, y, z)
}

func main() {
  yisZ_atX(12, "気温", 22.4)
}
```

え？急に簡単になったけど、これでいいの？

#08. 暗号文

与えられた文字列の各文字を，以下の仕様で変換する関数cipherを実装せよ．

英小文字ならば(219 - 文字コード)の文字に置換
その他の文字はそのまま出力
この関数を用い，英語のメッセージを暗号化・復号化せよ．

```
package main

import (
  "fmt"
)

func cipher(text string) string {

  char_list := []rune(text)
  ans := []rune{}
  for _, char := range char_list {
    if char >= 'a' && char <= 'z' {
      char = rune(219 - int(char))
    }   
    ans = append(ans, char)
  }
  return string(ans)
}

func main() {
  text := "abcde"

  fmt.Println(cipher(text))
}
```

わからなかったので、ググって答え見ました。
http://kokukoku.hatenablog.com/entry/2017/02/20/230813


#09. Typoglycemia
スペースで区切られた単語列に対して，各単語の先頭と末尾の文字は残し，それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ．ただし，長さが４以下の単語は並び替えないこととする．適当な英語の文（例えば"I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."）を与え，その実行結果を確認せよ．

```
package main

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

func shufflebySpace(sentence string, min int) string {
  words := strings.Fields(sentence)
  shuffledArr := []string{}
  for _, word := range words {
    shuffledArr = append(shuffledArr, shuffle(word, 4)) 
  }
  newSen := strings.Join(shuffledArr, " ")
  return newSen
}

func shuffle(str string, min int) string {
  runeStr := []rune(str)
  if len(runeStr) <= min {
    return str 
  }
  size := len(runeStr)
  rand.Seed(time.Now().UnixNano())
  for i := range runeStr {
    //最後尾をシャッフル対象から外すため-2
    j := rand.Intn(size - 2)
    if i == 0 || i == size-1 || j == 0 { 
      continue
    }   
    runeStr[i], runeStr[j] = runeStr[j], runeStr[i]
  }
  return string(runeStr)
}

func main() {
  text := "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."

  fmt.Println(shufflebySpace(text, 4)) 
}
```