sorting - 可以在类型约束之外使用的有序类型的 Go 接口(interface)?

标签 sorting go generics interface

我希望能够创建一个结构体,该结构体的字段可以保存任何类型的值,只要该类型可以与常用的比较运算符( <<=> 等)进行比较...)。我知道有contraints.Ordered ,但该接口(interface)仅限于用作类型约束。

以下是我想要使用此界面的示例:

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 和 string 测试用例分组到单个变量下。但这似乎不可能使用 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
}

最佳答案

具有唯一类型参数的泛型类型的实例化是不同的类型。我们需要一种方法来创建包含这些不同类型元素的 slice 。

Go 的类型参数功能不提供表示某种类型的所有实例化的非泛型类型,但 Go 确实具有我们从 Go 诞生之初就知道的基本接口(interface)功能。这就是我们在这里使用的。

我们定义一个 interface { run(*testing.T) } slice ,并用实例化类型的值填充该 slice ,其中该类型具有所需的 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!


评论表明可以通过使用接口(interface)的命名类型而不是匿名接口(interface)来改进答案。这是包含建议更改的代码。

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)
    }
}

关于sorting - 可以在类型约束之外使用的有序类型的 Go 接口(interface)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77441696/

相关文章:

go - 为什么 Go 生成的 hmac 哈希与 PHP 和 JavaScript 不同?

java - java中stream、collect、forEach组合的代码流程

Scala 编译器对泛型参数没有任何推断

go - 从 Go 在 Tarantool 中运行 SQL 查询是静默的(没有错误)

c# - 避免泛型函数中的超出范围问题

c - Euler 22/按字母顺序对名称进行排序

sorting - 使用脚本对具有多个条件的 Google 电子表格进行排序

php - 在 php 中对这个数组进行排序的最佳方法是什么?

C# 按枚举或列表自定义排序

go - Beego or 不等于运算符