Loops 从Golang的切片中取出

Loops 从Golang的切片中取出,loops,go,iterator,slice,Loops,Go,Iterator,Slice,我有下面的测试,即在过滤后打印原始输入片,而不打印已删除的元素,但在最后有一个额外的元素使输入片具有相同的长度,即使过滤后输入片应该更短 我已经看过这个医生了 然而,我认为我遗漏了一些关于Go的问题,因为我似乎使用了错误的方法 如何避免使用输出切片?以正确的方式打印,包含正确的元素,具有预期的长度和容量 为什么我试图删除inplace导致输入片的长度与过滤过程之前相同? 为什么输入片段的长度与我应用过滤过程之前的长度相同?如何执行删除操作以更改输入切片的长度? 代码如下: package foo

我有下面的测试,即在过滤后打印原始输入片,而不打印已删除的元素,但在最后有一个额外的元素使输入片具有相同的长度,即使过滤后输入片应该更短

我已经看过这个医生了 然而,我认为我遗漏了一些关于Go的问题,因为我似乎使用了错误的方法

如何避免使用输出切片?以正确的方式打印,包含正确的元素,具有预期的长度和容量 为什么我试图删除inplace导致输入片的长度与过滤过程之前相同? 为什么输入片段的长度与我应用过滤过程之前的长度相同?如何执行删除操作以更改输入切片的长度? 代码如下:

package foo

import (
    "fmt"
    "log"
    "math/rand"
    "testing"
)

type FooItem struct {
    Id       int
    Category string
    Value    float64
}

const minRand = 0
const maxRand = 10

const maxSliceLen = 3

var inFooSlice []FooItem

func init() {
    for i := 1; i <= maxSliceLen; i++ {
        inFooSlice = append(inFooSlice, FooItem{
            Id:       i,
            Category: "FooCat",
            Value:    minRand + rand.Float64()*(maxRand-minRand),
        })
    }
}

// this is the function I am testing
func FindAndRemoveFromFooSlice(iFilter int, inSl []FooItem) (*FooItem, []FooItem) {

    inLen := len(inSl)
    outSl := make([]FooItem, inLen)

    for idx, elem := range inSl {
        if elem.Id == iFilter {
            log.Printf("Loop ID %v", idx)

            // check these docs: https://github.com/golang/go/wiki/SliceTricks#delete
            outSl = inSl[:idx+copy(inSl[idx:], inSl[idx+1:inLen])]
            outSl = outSl[:inLen-1]

            return &elem, outSl
        }
    }
    return nil, nil
}

func TestFoo(t *testing.T) {
    fmt.Printf("\nOriginal (PRE) slice\n")
    fmt.Println(inFooSlice)
    fmt.Println(len(inFooSlice))
    fmt.Println(cap(inFooSlice))

    idFilter := 1

    fePtr, outFooSlice := FindAndRemoveFromFooSlice(idFilter, inFooSlice)

    fmt.Printf("\nOriginal (POST) slice\n")
    fmt.Println(inFooSlice)
    fmt.Println(len(inFooSlice))
    fmt.Println(cap(inFooSlice))

    fmt.Printf("\nFiltered element\n")
    fmt.Println(*fePtr)

    fmt.Printf("\nOutput slice\n")
    fmt.Println(outFooSlice)
    fmt.Println(len(outFooSlice))
    fmt.Println(cap(outFooSlice))
}
func FindAndRemoveFromFooSliceInPlace(iFilter int, inSl *[]FooItem) *FooItem {
    pointedInSl := *inSl
    inLen := len(pointedInSl)
    for idx, elem := range pointedInSl {
        if elem.Id == iFilter {
            log.Printf("Loop ID %v", idx)

            // check these docs: https://github.com/golang/go/wiki/SliceTricks#delete
            pointedInSl = append(pointedInSl[:idx], pointedInSl[idx+1:inLen]...)
            // pointedInSl[inLen-1] = FooItem{} // why this throws a runtime "panic: runtime error: index out of range" ???
            pointedInSl = pointedInSl[:inLen-1]

            return &elem
        }
    }
    return nil
}

func TestFooInPlace(t *testing.T) {
    fmt.Printf("\nOriginal (PRE) slice\n")
    fmt.Println(inFooSlice)
    fmt.Println(len(inFooSlice))
    fmt.Println(cap(inFooSlice))

    idFilter := 1

    fePtr := FindAndRemoveFromFooSliceInPlace(idFilter, &inFooSlice)

    fmt.Printf("\nOriginal (POST) slice\n")
    fmt.Println(inFooSlice)
    fmt.Println(len(inFooSlice))
    fmt.Println(cap(inFooSlice))

    fmt.Printf("\nFiltered element\n")
    fmt.Println(*fePtr)
}
作为指针在输入片上更新

好,假设我想处理原始的输入片,也就是说,没有拷贝或输出片

为什么下面的代码会在注释的代码行中引发运行时死机?pointedInSl[inLen-1]=FooItem{} 为什么应用函数后打印的切片末尾包含两个相同的ITME?如何删除最后一个冗余元素? 为什么应用函数后的切片长度仍然与应用函数前的切片长度相同? 如何使原始切片收缩为1,即输出长度=原始长度-1? 代码如下:

package foo

import (
    "fmt"
    "log"
    "math/rand"
    "testing"
)

type FooItem struct {
    Id       int
    Category string
    Value    float64
}

const minRand = 0
const maxRand = 10

const maxSliceLen = 3

var inFooSlice []FooItem

func init() {
    for i := 1; i <= maxSliceLen; i++ {
        inFooSlice = append(inFooSlice, FooItem{
            Id:       i,
            Category: "FooCat",
            Value:    minRand + rand.Float64()*(maxRand-minRand),
        })
    }
}

// this is the function I am testing
func FindAndRemoveFromFooSlice(iFilter int, inSl []FooItem) (*FooItem, []FooItem) {

    inLen := len(inSl)
    outSl := make([]FooItem, inLen)

    for idx, elem := range inSl {
        if elem.Id == iFilter {
            log.Printf("Loop ID %v", idx)

            // check these docs: https://github.com/golang/go/wiki/SliceTricks#delete
            outSl = inSl[:idx+copy(inSl[idx:], inSl[idx+1:inLen])]
            outSl = outSl[:inLen-1]

            return &elem, outSl
        }
    }
    return nil, nil
}

func TestFoo(t *testing.T) {
    fmt.Printf("\nOriginal (PRE) slice\n")
    fmt.Println(inFooSlice)
    fmt.Println(len(inFooSlice))
    fmt.Println(cap(inFooSlice))

    idFilter := 1

    fePtr, outFooSlice := FindAndRemoveFromFooSlice(idFilter, inFooSlice)

    fmt.Printf("\nOriginal (POST) slice\n")
    fmt.Println(inFooSlice)
    fmt.Println(len(inFooSlice))
    fmt.Println(cap(inFooSlice))

    fmt.Printf("\nFiltered element\n")
    fmt.Println(*fePtr)

    fmt.Printf("\nOutput slice\n")
    fmt.Println(outFooSlice)
    fmt.Println(len(outFooSlice))
    fmt.Println(cap(outFooSlice))
}
func FindAndRemoveFromFooSliceInPlace(iFilter int, inSl *[]FooItem) *FooItem {
    pointedInSl := *inSl
    inLen := len(pointedInSl)
    for idx, elem := range pointedInSl {
        if elem.Id == iFilter {
            log.Printf("Loop ID %v", idx)

            // check these docs: https://github.com/golang/go/wiki/SliceTricks#delete
            pointedInSl = append(pointedInSl[:idx], pointedInSl[idx+1:inLen]...)
            // pointedInSl[inLen-1] = FooItem{} // why this throws a runtime "panic: runtime error: index out of range" ???
            pointedInSl = pointedInSl[:inLen-1]

            return &elem
        }
    }
    return nil
}

func TestFooInPlace(t *testing.T) {
    fmt.Printf("\nOriginal (PRE) slice\n")
    fmt.Println(inFooSlice)
    fmt.Println(len(inFooSlice))
    fmt.Println(cap(inFooSlice))

    idFilter := 1

    fePtr := FindAndRemoveFromFooSliceInPlace(idFilter, &inFooSlice)

    fmt.Printf("\nOriginal (POST) slice\n")
    fmt.Println(inFooSlice)
    fmt.Println(len(inFooSlice))
    fmt.Println(cap(inFooSlice))

    fmt.Printf("\nFiltered element\n")
    fmt.Println(*fePtr)
}
这是奇怪的输出:

$ go test -v -run TestFooInPlace
=== RUN   TestFooInPlace

Original (PRE) slice
[{1 FooCat 6.046602879796196} {2 FooCat 9.405090880450125} {3 FooCat 6.645600532184904}]
3
4
2019/05/31 16:32:38 Loop ID 0

Original (POST) slice
[{2 FooCat 9.405090880450125} {3 FooCat 6.645600532184904} {3 FooCat 6.645600532184904}]
3
4

Filtered element
{1 FooCat 6.046602879796196}
--- PASS: TestFooInPlace (0.00s)
PASS
ok      git.openenergi.net/scm/flex/service/common  0.007s

当你有一个int类型的变量,你想写一个函数来增加它的值,你怎么做?您可以传递一个指向变量的指针,也可以返回必须分配给原始变量的递增值

例如,在以下位置尝试:

在上面的代码中,您将i传递给inc,inc将其递增并返回其值。原来的我当然不会改变,我只是一个复制品,独立于原来的我。要更改原始值,您必须分配返回值:

i = inc(i)
或者首先使用指针在以下位置尝试:

切片也是一样。如果您想要/必须修改作为数据指针、长度和容量的切片标头,请参阅,您必须将指针传递到不太常见的切片,或者必须返回修改后的新切片标头,您必须在调用者处分配该标头。这是更经常使用的解决方案,这也是内置的方法

当您切片一个切片时,例如someslice[min:max],新切片将与原始切片共享备份阵列。这意味着,如果修改新切片的元素,原始切片也将观察到这些更改。因此,如果从新切片中删除一个元素,并将这些元素复制到已删除元素的位置,则原始切片的最后一个元素仍将存在,该元素由原始切片覆盖。通常的做法是将最后一个元素归零,以便垃圾收集器可以回收其内存(如果是指针类型或类似类型,如片、映射或通道)。有关详细信息,请参阅和

要直接回答您的问题:

如何避免使用输出切片?以正确的方式打印,包含正确的元素,具有预期的长度和容量 正如这个答案中所述:您必须传递一个指向切片的指针,并修改FindDemoveFromFooSlice中的指向值,这样您就不必返回新切片

为什么我试图删除inplace导致输入片的长度与过滤过程之前相同? 您从未修改过原始切片,您传递了原始切片以制作副本,而在FindDemoveFromFoosice中,您只能修改副本,但您甚至没有修改副本。您返回一个新的切片,但没有分配它,因此原始切片头是完整的

为什么输入片段的长度与我应用过滤过程之前的长度相同?如何执行删除操作以更改输入切片的长度? 前面的两个问题回答了这个问题

见相关问题:


当你有一个int类型的变量,你想写一个函数来增加它的值,你怎么做?您可以传递一个指向变量的指针,也可以返回必须分配给原始变量的递增值

例如,在以下位置尝试:

在上面的代码中,您将i传递给inc,inc将其递增并返回其值。原来的我当然不会改变,我只是一个复制品,独立于原来的我。要更改原始值,您必须分配返回值:

i = inc(i)
或者首先使用指针在以下位置尝试:

切片也是一样。如果您想要/必须修改作为数据指针的切片标头、长度和容量,请参阅 您要么必须传递一个指向不太常见的切片的指针,要么必须返回修改后的新切片头,您必须在调用者处分配它。这是更经常使用的解决方案,这也是内置的方法

当您切片一个切片时,例如someslice[min:max],新切片将与原始切片共享备份阵列。这意味着,如果修改新切片的元素,原始切片也将观察到这些更改。因此,如果从新切片中删除一个元素,并将这些元素复制到已删除元素的位置,则原始切片的最后一个元素仍将存在,该元素由原始切片覆盖。通常的做法是将最后一个元素归零,以便垃圾收集器可以回收其内存(如果是指针类型或类似类型,如片、映射或通道)。有关详细信息,请参阅和

要直接回答您的问题:

如何避免使用输出切片?以正确的方式打印,包含正确的元素,具有预期的长度和容量 正如这个答案中所述:您必须传递一个指向切片的指针,并修改FindDemoveFromFooSlice中的指向值,这样您就不必返回新切片

为什么我试图删除inplace导致输入片的长度与过滤过程之前相同? 您从未修改过原始切片,您传递了原始切片以制作副本,而在FindDemoveFromFoosice中,您只能修改副本,但您甚至没有修改副本。您返回一个新的切片,但没有分配它,因此原始切片头是完整的

为什么输入片段的长度与我应用过滤过程之前的长度相同?如何执行删除操作以更改输入切片的长度? 前面的两个问题回答了这个问题

见相关问题:


我建议对icza答案进行编辑,以便在其底部为他提供的有用信息提供一个最低限度的工作代码示例。它被拒绝,说它作为一个编辑是没有意义的,它应该作为一个评论或答案来写,所以这里主要归功于icza:

带有注释的最低工作代码示例,以提供一些上下文:

// use a pointer for the input slice so then it is changed in-place
func FindAndRemoveFromFooSliceInPlace(iFilter int, inSl *[]FooItem) *FooItem {
    pointedInSl := *inSl // dereference the pointer so then we can use `append`
    inLen := len(pointedInSl)
    for idx, elem := range pointedInSl {
        if elem.Id == iFilter {
            log.Printf("Loop ID %v", idx)

            // check these docs: https://github.com/golang/go/wiki/SliceTricks#delete
            pointedInSl = append(pointedInSl[:idx], pointedInSl[idx+1:inLen]...)
            pointedInSl = pointedInSl[:inLen-1]
            *inSl = pointedInSl // assigning the new slice to the pointed value before returning

            return &elem
        }
    }
    return nil
}

我建议对icza答案进行编辑,以便在其底部为他提供的有用信息提供一个最低限度的工作代码示例。它被拒绝,说它作为一个编辑是没有意义的,它应该作为一个评论或答案来写,所以这里主要归功于icza:

最低工作代码示例,带有注释,以提供一些上下文:

// use a pointer for the input slice so then it is changed in-place
func FindAndRemoveFromFooSliceInPlace(iFilter int, inSl *[]FooItem) *FooItem {
    pointedInSl := *inSl // dereference the pointer so then we can use `append`
    inLen := len(pointedInSl)
    for idx, elem := range pointedInSl {
        if elem.Id == iFilter {
            log.Printf("Loop ID %v", idx)

            // check these docs: https://github.com/golang/go/wiki/SliceTricks#delete
            pointedInSl = append(pointedInSl[:idx], pointedInSl[idx+1:inLen]...)
            pointedInSl = pointedInSl[:inLen-1]
            *inSl = pointedInSl // assigning the new slice to the pointed value before returning

            return &elem
        }
    }
    return nil
}

感谢您的概述和外部资源,我仍然不明白为什么在收缩作为指针提供的原始输入片段时无法删除最后一个重复项。我用更多的代码更新了原来的问题。Thanks@tppz因为您取消了对制作副本的指针:pointedInSl:=*inSl的引用,并且您只分配给该副本。在返回之前,请确保将新切片指定给指向的值,如下所示:*inSl=pointedInSl。感谢您的概述和外部资源,我仍然不明白为什么在收缩作为指针提供的原始输入切片时无法删除最后一个重复项。我用更多的代码更新了原来的问题。Thanks@tppz因为您取消了对制作副本的指针:pointedInSl:=*inSl的引用,并且您只分配给该副本。返回之前,请确保将新切片指定给如下所示的定点值:*inSl=pointedInSl。