Sibainu Relax Room

柴犬と過ごす

CSV を GO でクィックソート 3

概要

2022年12月18日投稿の「CSV を GO でクィックソート 2」を一部修正します。

これは、「現場で使える実践テクニック みんなの言語 改訂2版」の中の第2章を読み理解したところで修正するものです。

「守るべき暗黙のルール」こう言ったことは、独学で書いている者にとって忘れているというよりも、全く気にすることがないことなので、目が覚める思いでした。

私が教科書にしている本です。

修正前のコード(mainのみ)

copy

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

前回、コンパイルがなぜか通りましたが今回エラーになりました。

気を取り直し、見直してコンパイルが通ったコードは次の通りです。

修正後のコード(全コード)

copy

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 ループを使います。ですので、ちょっと工夫しなければなりません。

copy

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 と同じような結果になりました。

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