如何反转Go中的切片?

问题描述 投票:0回答:7

如何在 Go 中反转任意切片 (

[]interface{}
)?我宁愿不必编写
Less
Swap
来使用
sort.Reverse
。有没有一种简单的内置方法可以做到这一点?

go slice
7个回答
131
投票

在 Go 1.21 或更高版本中使用 slices.Reverse

slices.Reverse(s)

Go 1.20 及更早版本的答案:

标准库没有用于反转切片的内置函数。使用 for 循环反转切片:

for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
    s[i], s[j] = s[j], s[i]
}

在 Go 1.18 或更高版本中使用类型参数编写通用反向函数:

func reverse[S ~[]E, E any](s S)  {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
}

使用 reflect.Swaper 编写一个可在 Go 1.8 或更高版本中处理任意切片类型的函数:

func reverse(s interface{}) {
    n := reflect.ValueOf(s).Len()
    swap := reflect.Swapper(s)
    for i, j := 0, n-1; i < j; i, j = i+1, j-1 {
        swap(i, j)
    }
}

此答案中的函数会就地反转切片。如果您不想修改原始切片,请在反转切片之前复制切片


10
投票

这是反转通用切片的另一种可能方法(转到 1.18)

// You can edit this code!
// Click here and start typing.
package main

import (
    "fmt"
    "sort"
)

func main() {
    nums := []int64{10, 5, 15, 20, 1, 100, -1}
    ReverseSlice(nums)
    fmt.Println(nums)

    strs := []string{"hello", "world"}
    ReverseSlice(strs)
    fmt.Println(strs)

    runes := []rune{'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'}
    ReverseSlice(runes)
    for _, r := range runes {
        fmt.Print(string(r), " ")
    }
}

func ReverseSlice[T comparable](s []T) {
    sort.SliceStable(s, func(i, j int) bool {
        return i > j
    })
}

运行上面的程序应该输出:

[-1 100 1 20 15 5 10]
[world hello]
d l r o w o l l e h 
Program exited.

去游乐场


8
投票

这将返回一个反转的切片,而不修改原始切片。

官方 wiki 页面使用的算法:https://github.com/golang/go/wiki/SliceTricks#reversing

func reverse(s []interface{}) []interface{} {
    a := make([]interface{}, len(s))
    copy(a, s)

    for i := len(a)/2 - 1; i >= 0; i-- {
        opp := len(a) - 1 - i
        a[i], a[opp] = a[opp], a[i]
    }

    return a
}

1
投票

这里有我的代码示例,你可以在playground

运行它
package main

import (
    "fmt"
    "reflect"
    "errors"
)

func ReverseSlice(data interface{}) {
    value := reflect.ValueOf(data)
    if value.Kind() != reflect.Slice {
        panic(errors.New("data must be a slice type"))
    }
    valueLen := value.Len()
    for i := 0; i <= int((valueLen-1)/2); i++ {
        reverseIndex := valueLen - 1 - i
        tmp := value.Index(reverseIndex).Interface()
        value.Index(reverseIndex).Set(value.Index(i))
        value.Index(i).Set(reflect.ValueOf(tmp))
    }
}


func main() {
    names := []string{"bob", "mary", "sally", "michael"}
    ReverseSlice(names)
    fmt.Println(names)
}


1
投票

这是我与 generics 一起使用的函数(转到 1.18+)。您可以使用它来反转任何类型的切片甚至字符串(使用拆分/连接技巧)。它不会改变原始切片。

package main

import (
    "fmt"
    "strings"
)

func Reverse[T any](original []T) (reversed []T) {
    reversed = make([]T, len(original))
    copy(reversed, original)

    for i := len(reversed)/2 - 1; i >= 0; i-- {
        tmp := len(reversed) - 1 - i
        reversed[i], reversed[tmp] = reversed[tmp], reversed[i]
    }

    return
}

func main() {
    a := []string{"a", "b", "c"}
    fmt.Println(a, Reverse(a))

    b := []uint{0, 1, 2}
    fmt.Println(b, Reverse(b))

    c := "abc"
    fmt.Println(c, strings.Join(Reverse(strings.Split(c, "")), ""))
}

更好去游乐场


1
投票
func Reverse[T any](array []T) []T {
    length := len(array)
    result := make([]T, length)
    for i, elem := range array {
        result[length-1-i] = elem
    }
    return result
}

func ReverseInPlace[T any](array []T) []T {
    length := len(array)
    swap := reflect.Swapper(array)
    for i := 0; i < length/2; i++ {
        swap(i, length-1-i)
    }
    return array
}

增强了已接受的答案。

和测试:

func TestReverse(t *testing.T) {
    t.Run("Empty array", func(t *testing.T) {
        input := []int{}
        assert.Equal(t, []int{}, Reverse(input))
        assert.Equal(t, []int{}, input)
    })

    t.Run("Reverse integers", func(t *testing.T) {
        input := []int{1, 2, 3}
        assert.Equal(t, []int{3, 2, 1}, Reverse(input))
        assert.Equal(t, []int{1, 2, 3}, input)
    })

    t.Run("Reverse strings", func(t *testing.T) {
        input := []string{"a", "b", "c"}
        assert.Equal(t, []string{"c", "b", "a"}, Reverse(input))
        assert.Equal(t, []string{"a", "b", "c"}, input)
    })
}

func TestReverseInPlace(t *testing.T) {
    t.Run("Empty array", func(t *testing.T) {
        input := []int{}
        assert.Equal(t, []int{}, ReverseInPlace(input))
        assert.Equal(t, []int{}, input)
    })

    t.Run("Reverse integers", func(t *testing.T) {
        input := []int{1, 2, 3}
        assert.Equal(t, []int{3, 2, 1}, ReverseInPlace(input))
        assert.Equal(t, []int{3, 2, 1}, input)
    })

    t.Run("Reverse strings", func(t *testing.T) {
        input := []string{"a", "b", "c"}
        assert.Equal(t, []string{"c", "b", "a"}, ReverseInPlace(input))
        assert.Equal(t, []string{"c", "b", "a"}, input)
    })
}

0
投票

这个通用切片反转函数应该可以为你做到:

func ReverseSlice[T comparable](s []T) []T {
  var r []T
  for i := len(s) - 1; i >= 0; i-- {
    r = append(r, s[i])
  }
  return r
}
© www.soinside.com 2019 - 2024. All rights reserved.