nil かもしれないメンバのある構造体の整列
これは何?
という記事を見て、自分ならどう書くかなと思って書いたもの。
generics の練習を兼ねて
まずはソース
go1.18
package main
import (
"encoding/json"
"fmt"
"math/rand"
"sort"
"golang.org/x/exp/constraints"
)
type Sample struct {
Name *string
Description *string
Note *string
}
func ToPtr[T any](x T) *T {
return &x
}
func NewSample(name, desc, note *string) *Sample {
return &Sample{
Name: name,
Description: desc,
Note: note,
}
}
func comparePtr[T constraints.Ordered](x, y *T) int {
if x == y {
return 0
}
if x == nil {
return -1
}
if y == nil {
return 1
}
if *x == *y {
return 0
}
if *x < *y {
return -1
}
return 1
}
func compareSampleMemberStrPtr(sx, sy *Sample, proc func(*Sample) *string) int {
return comparePtr(proc(sx), proc(sy))
}
func SampleCompare(a, b *Sample) int {
if c := compareSampleMemberStrPtr(a, b, func(s *Sample) *string { return s.Name }); c != 0 {
return c
}
if c := compareSampleMemberStrPtr(a, b, func(s *Sample) *string { return s.Description }); c != 0 {
return c
}
return compareSampleMemberStrPtr(a, b, func(s *Sample) *string { return s.Note })
}
func show(title string, samples []*Sample) {
fmt.Println(title)
for _, v := range samples {
jsonBytes, err := json.Marshal(v)
if err != nil {
panic(err)
}
fmt.Println(" " + string(jsonBytes))
}
}
func shuffled(i int) []int {
s := make([]int, i)
for ix := 0; ix < i; ix++ {
s[ix] = ix
}
rand.Shuffle(i, func(x, y int) {
s[x], s[y] = s[y], s[x]
})
return s
}
func main() {
samples := []*Sample{}
for _, k := range shuffled(27) {
name := []*string{ToPtr("名前01"), ToPtr("名前02"), nil}[k%3]
desc := []*string{ToPtr("説明01"), ToPtr("説明02"), nil}[(k/3)%3]
note := []*string{ToPtr("メモ01"), ToPtr("メモ02"), nil}[(k/9)%3]
samples = append(samples, NewSample(name, desc, note))
}
show("before sorting", samples)
sort.Slice(samples, func(i, j int) bool {
return SampleCompare(samples[i], samples[j]) < 0
})
show("after sorting", samples)
}
ちょっと説明
ToPtr[T any]
package main
import (
"encoding/json"
"fmt"
"math/rand"
"sort"
"golang.org/x/exp/constraints"
)
type Sample struct {
Name *string
Description *string
Note *string
}
func ToPtr[T any](x T) *T {
return &x
}
func NewSample(name, desc, note *string) *Sample {
return &Sample{
Name: name,
Description: desc,
Note: note,
}
}
func comparePtr[T constraints.Ordered](x, y *T) int {
if x == y {
return 0
}
if x == nil {
return -1
}
if y == nil {
return 1
}
if *x == *y {
return 0
}
if *x < *y {
return -1
}
return 1
}
func compareSampleMemberStrPtr(sx, sy *Sample, proc func(*Sample) *string) int {
return comparePtr(proc(sx), proc(sy))
}
func SampleCompare(a, b *Sample) int {
if c := compareSampleMemberStrPtr(a, b, func(s *Sample) *string { return s.Name }); c != 0 {
return c
}
if c := compareSampleMemberStrPtr(a, b, func(s *Sample) *string { return s.Description }); c != 0 {
return c
}
return compareSampleMemberStrPtr(a, b, func(s *Sample) *string { return s.Note })
}
func show(title string, samples []*Sample) {
fmt.Println(title)
for _, v := range samples {
jsonBytes, err := json.Marshal(v)
if err != nil {
panic(err)
}
fmt.Println(" " + string(jsonBytes))
}
}
func shuffled(i int) []int {
s := make([]int, i)
for ix := 0; ix < i; ix++ {
s[ix] = ix
}
rand.Shuffle(i, func(x, y int) {
s[x], s[y] = s[y], s[x]
})
return s
}
func main() {
samples := []*Sample{}
for _, k := range shuffled(27) {
name := []*string{ToPtr("名前01"), ToPtr("名前02"), nil}[k%3]
desc := []*string{ToPtr("説明01"), ToPtr("説明02"), nil}[(k/3)%3]
note := []*string{ToPtr("メモ01"), ToPtr("メモ02"), nil}[(k/9)%3]
samples = append(samples, NewSample(name, desc, note))
}
show("before sorting", samples)
sort.Slice(samples, func(i, j int) bool {
return SampleCompare(samples[i], samples[j]) < 0
})
show("after sorting", samples)
}
ToPtr[T any]
何かを何かへのポインタに変換する。
変換元へのポインタではなく、変換元のコピーへのポインタになるので要注意だけど、便利。
そもそも go で &("hoge")
って書かせてくれれば要らなくなる関数だけど、いまは書けないので。
comparePtr[T constraints.Ordered]
nil
は非 nil
よりも小さい、という論理で比較する。
両方とも非 nil
なら、ポインタのサス先の値の大小関係を使う。
比較結果は -1, 0, 1 で表現する。
compareSampleMemberStrPtr
Sample
のメンバ(じゃなくてもいいけど)である文字列へのポインタを比較する。
メンバを参照するための関数を受けるのは
comparePtr(a.Name, b.Note)
みたいなミスを避けるため。
C++ なら「メンバへのポインタ型」の値を使うんだけどそういうの無いからね。
SampleCompare
主要部はこれ。
メンバを一個比較して、等しかったら次のメンバを比較、また等しかったら次のメンバを比較。
という流れ。
今回はループにしなかったけど、ループにするのも悪くない。
書いてみて思ったこと
やっぱりなんか go で書くと長くなるよなと思う。
if *x == *y {
return 0
}
if *x < *y {
return -1
}
return 1
は、C/C++ だと
return (*y<*x) - (*x<*y);
だよなぁとか思ったり。
Author And Source
この問題について(nil かもしれないメンバのある構造体の整列), 我々は、より多くの情報をここで見つけました https://qiita.com/Nabetani/items/62f1a1d730d4e26be6e4著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .