
概要
2022年12月18日投稿の「CSV を GO でクィックソート 2」を一部修正します。
これは、「現場で使える実践テクニック みんなの言語 改訂2版」の中の第2章を読み理解したところで修正するものです。
「守るべき暗黙のルール」こう言ったことは、独学で書いている者にとって忘れているというよりも、全く気にすることがないことなので、目が覚める思いでした。
私が教科書にしている本です。
修正前のコード(mainのみ)
func main() {
// ----------ここに追加
in, err := os.Open("./input.csv")
if err != nil {
log.Fatal(err)
}
defer in.Close()
r := csv.NewReader(transform.NewReader(in, japanese.ShiftJIS.NewDecoder()))
// records => [][]string
records, err := r.ReadAll()
if err != nil {
log.Fatal(err)
}
QuickSort(records, 0, len(records)-1)
// 書き込み先ファイル
// ----------ここに追加
out, err := os.Create("./output.csv")
if err != nil {
log.Fatal(err)
}
defer out.Close()
// 書き込み
w := csv.NewWriter(transform.NewWriter(out, japanese.ShiftJIS.NewEncoder()))
w.WriteAll(records)
}
import に “path/filepath” を追加して、
in, err := os.Open("./input.csv")
を
cwd, err := os.Getwd()
if err != nil {
log.Fatal(err)
return
}
name := filepath.Join(cwd, "input.csv")
in, err := os.Open(name)
に変更すればいいのかなと実行してみたら
次のエラーが出ました。
cannot use records (variable of type [][]string) as type []string in argument to QuickSort
前回、コンパイルがなぜか通りましたが今回エラーになりました。
気を取り直し、見直してコンパイルが通ったコードは次の通りです。
修正後のコード(全コード)
package main
import (
"encoding/csv"
"log"
"os"
"strings"
"path/filepath"
"golang.org/x/text/encoding/japanese"
"golang.org/x/text/transform"
)
func QuickSort(elements [][]string, left int, right int) {
if left < right {
posi := getposi(elements, left, right)
QuickSort(elements, left, posi-1)
QuickSort(elements, posi+1, right)
}
}
func getposi(elements [][]string, left int, right int) int {
//軸を右端
pivot := elements[right][0]
//開始位置
i := left
//昇順にするため左に小さいもののエリア
for j := left; j < right; j++ {
//pivotより小さいものが見つかった
if strings.Compare(pivot, elements[j][0]) == 1 {
swap(&elements[i], &elements[j])
//次の交換位置
i += 1
}
}
//pivotの位置iが定まったので交換
swap(&elements[i], &elements[right])
//位置を返す
return i
}
func swap(element1 *([]string), element2 *([]string)) {
val := (*element1)[0]
(*element1)[0] = (*element2)[0]
(*element2)[0] = val
}
func main() {
cwd, err := os.Getwd()
if err != nil {
log.Fatal(err)
return
}
name := filepath.Join(cwd, "input.csv")
in, err := os.Open(name)
if err != nil {
log.Fatal(err)
return
}
defer in.Close()
r := csv.NewReader(transform.NewReader(in, japanese.ShiftJIS.NewDecoder()))
// records => [][]string
records, err := r.ReadAll()
if err != nil {
log.Fatal(err)
return
}
QuickSort(records, 0, len(records)-1)
// 書き込み先ファイル
name = filepath.Join(cwd, "output.csv")
out, err := os.Create(name)
if err != nil {
log.Fatal(err)
return
}
defer out.Close()
// 書き込み
w := csv.NewWriter(transform.NewWriter(out, japanese.ShiftJIS.NewEncoder()))
w.WriteAll(records)
}
1列から2列のソート
左が前回の CSV ファイルです。今回は JAVA のクイックソートで使った CSV ファイルを使ってみます。

2列用に改良したコード
GO言語には、While ループがありませんので For ループを使います。ですので、ちょっと工夫しなければなりません。
package main
import (
"encoding/csv"
"log"
"os"
"strings"
"path/filepath"
"golang.org/x/text/encoding/japanese"
"golang.org/x/text/transform"
)
func QuickSort(elements [][]string, left int, right int) {
if left < right {
posi := getposi(elements, left, right)
QuickSort(elements, left, posi-1)
QuickSort(elements, posi+1, right)
}
}
func getposi(elements [][]string, left int, right int) int {
// 軸を中央値
mid := (left + right) / 2
// 右端の値と中央値を入れ替えます。
swap(&elements[right], &elements[mid])
// 開始位置・区切り位置
i := left
// 昇順にするため左に小さいもののエリア
for j := left; j < right; j++ {
// pivotより小さいものが見つかった
if ijcomp(elements, right, j) {
swap(&elements[i], &elements[j])
// 次の交換位置
i += 1
}
}
// pivotの位置iが定まったので交換
swap(&elements[i], &elements[right])
// pivotの位置を返す
return i
}
func ijcomp(elements [][]string, right int, j int) bool {
// elements[right][0] > elements[j][0] なら res=1
res := strings.Compare(elements[right][0], elements[j][0])
if res == 0 {
res = strings.Compare(elements[right][1], elements[j][1])
}
return res >= 0
}
func swap(element1 *([]string), element2 *([]string)) {
val := (*element1)[0]
(*element1)[0] = (*element2)[0]
(*element2)[0] = val
val = (*element1)[1]
(*element1)[1] = (*element2)[1]
(*element2)[1] = val
}
func main() {
cwd, err := os.Getwd()
if err != nil {
log.Fatal(err)
return
}
name := filepath.Join(cwd, "input.csv")
in, err := os.Open(name)
if err != nil {
log.Fatal(err)
return
}
defer in.Close()
r := csv.NewReader(transform.NewReader(in, japanese.ShiftJIS.NewDecoder()))
// records => [][]string
records, err := r.ReadAll()
if err != nil {
log.Fatal(err)
return
}
QuickSort(records, 0, len(records)-1)
// 書き込み先ファイル
name = filepath.Join(cwd, "output.csv")
out, err := os.Create(name)
if err != nil {
log.Fatal(err)
return
}
defer out.Close()
// 書き込み
w := csv.NewWriter(transform.NewWriter(out, japanese.ShiftJIS.NewEncoder()))
w.WriteAll(records)
}
実行結果
JAVA と同じ内容になりました。
実行ファイルをクリックしてソート結果が書き出されるまでの時間は2~3秒と JAVA と同じような結果になりました。

今日はここまでとします。