可以在类型约束之外使用的有序类型的Go接口?

bybem2ql  于 2023-11-14  发布在  Go
关注(0)|答案(1)|浏览(66)

我希望能够创建一个结构体,它有一个字段,可以容纳任何类型的值,只要该类型可以与常用的比较运算符(<<=>等)进行比较。我知道有contraints.Ordered,但该接口仅限于用作类型约束。
下面是我想使用此接口的一个示例:

func TestMergeSort(t *testing.T) {
    type TestCase[T constraints.Ordered] struct {
            input []T
            expected []T
    }
    intTests := map[string]TestCase[int] {
            "#1": {
                    input: []int{3,2,5,1,4,6},
                    expected: []int{1,2,3,4,5,6},
            },
    }
    stringTests := map[string]TestCase[string] {
            "#2": {
                    input: []string{"hello","bonjour"},
                    expected: []string{"bonjour","hello"},
            },
    }

    // test int sorting
    for name, test := range intTests {
        t.Run(name, func(t *testing.T) {
            output := MergeSort(test.input)

            if !cmp.Equal(test.expected, output) {
                t.Fatalf(`Expected %v got %v`, test.expected, output)
            }       
        })
    }
    // test string sorting
    for name, test := range stringTests {
        t.Run(name, func(t *testing.T) {
            output := MergeSort(test.input)

            if !cmp.Equal(test.expected, output) {
                t.Fatalf(`Expected %v got %v`, test.expected, output)
            }       
        })
    }
}

字符集

我希望能够将int和字符串测试用例组合在一个变量下。但是使用带有constraints.Ordered约束的泛型似乎不可能,因为我必须在使用泛型类型之前示例化它。

理想情况下,我想把它简化成这样:

func TestMergeSortTable(t *testing.T) {
    // What I'm looking for is something that can replace the `Sortable` interface example below
    type TestCase struct {
        input []Sortable
        expected []Sortable
    }
    tests := map[string]TestCase {
        "#1": {
            input: []int{3,2,5,1,4,6},
            expected: []int{1,2,3,4,5,6},
        },
        "#2": {
            input: []string{"hello","bonjour"},
            expected: []string{"bonjour","hello"},
        },
    }

    // test int and string sorting in a single loop
    for name, test := range tests {
        t.Run(name, func(t *testing.T) {
            output := MergeSort(test.input)

            if !cmp.Equal(test.expected, output) {
                t.Fatalf(`Expected %v got %v`, test.expected, output)
            }       
        })
    }
}


PS:MergeSort的函数签名,以备不时之需

func MergeSort[T constraints.Ordered](arr []T)(res []T) {
    // sorting logic here
}

s5a0g9ez

s5a0g9ez1#

具有唯一类型参数的泛型类型的示例化是不同的类型。我们需要一种方法来创建一个包含这些不同类型的元素的切片。
Go语言的类型参数特性没有提供一个非泛型类型来表示某个类型的所有示例化,但是Go语言确实有一个基本的接口特性,这是我们在Go语言早期就知道的,这就是我们在这里使用的。
我们定义了一个interface { run(*testing.T) }切片,并用示例化类型的值填充切片,其中类型具有所需的run方法。

type sortTestCase[T constraints.Ordered] struct {
    input, want []T
}

func (tc sortTestCase[T]) run(t *testing.T) {
    got := MergeSort(tc.input)
    if !cmp.Equal(tc.want, got) {
        t.Fatalf(`Expected %v got %v`, tc.want, got)
    }       
}

func TestSort(t *testing.T) {
    testCases := map[string]interface{ run(t *testing.T) }{
        "ints": sortTestCase[int]{
            input: []int{3, 2, 5, 1, 4, 6},
            want:  []int{1, 2, 3, 4, 5, 6},
        },
        "strings": sortTestCase[string]{
            input: []string{"hello", "bonjour"},
            want:  []string{"bonjour", "hello"},
        },
    }
    for n, tc := range testCases {
        t.Run(n, tc.run)
    }
}

字符集
Run the test on the playground!
一条注解建议,可以通过使用接口的命名类型而不是匿名接口来改进答案。

type sortTestCase[T constraints.Ordered] struct {
    input, want []T
}

func (tc sortTestCase[T]) run(t *testing.T) {
    got := MergeSort(tc.input)
    if !cmp.Equal(tc.want, got) {
        t.Fatalf(`Expected %v got %v`, tc.want, got)
    }       
}

func TestSort(t *testing.T) {
    type runtester interface{ run(t *testing.T) }
    var sortTestCases = map[string]runtester{
        "ints": sortTestCase[int]{
            input: []int{3, 2, 5, 1, 4, 6},
            want:  []int{1, 2, 3, 4, 5, 6},
        },
        "strings": sortTestCase[string]{
             input: []string{"hello", "bonjour"},
             want:  []string{"bonjour", "hello"},
        },
    }
    for n, tc := range testCases {
        t.Run(n, tc.run)
    }
}

相关问题