无效操作:无法在建模任意维度的切片时索引 T。

huangapple go评论120阅读模式
英文:

invalid operation: cannot index T when modeling slices of arbitrary dimensions

问题

我正在尝试对一个未知大小的矩阵进行矩阵减法运算。以下是代码:

type ArrayLike interface {
	[]interface{} | [][]interface{} | [][][]interface{} | [][][][]interface{}
}

func subMatrix[T ArrayLike](A, B T, shape []int) interface{} {

	dim := shape[0]
	if len(shape) == 1 {
		retObj := make([]interface{}, dim)
		for i := 0; i < dim; i++ {
			Av := A[i].(float64)
			Bv := B[i].(float64)
			retObj[i] = Av - Bv
		}
		return retObj
	} else {
		retObj := make([]interface{}, dim)
		for i := 0; i < dim; i++ {
			retObj[i] = subMatrix(Av[i], Bv[i], shape[1:])
		}
		return retObj
	}
}

它报错:

invalid operation: cannot index A (variable of type T constrained by []interface{}|[][]interface{}|[][][]interface{}|[][][][]interface{})compilerNonIndexableOperand

有人知道如何解决这个问题吗?

英文:

I am try to do a matrix subtraction with unknow size of matrix. here is the code:

type ArrayLike interface {
	[]interface{} | [][]interface{} | [][][]interface{} | [][][][]interface{}
}

func subMatrix[T ArrayLike](A, B T, shape []int) interface{} {

	dim := shape[0]
	if len(shape) == 1 {
		retObj := make([]interface{}, dim)
		for i := 0; i &lt; dim; i++ {
			Av := A[i].(float64)
			Bv := B[i].(float64)
			retObj[i] = Av - Bv
		}
		return retObj
	} else {
		retObj := make([]interface{}, dim)
		for i := 0; i &lt; dim; i++ {
			retObj[i] = subMatrix(Av[i], Bv[i], shape[1:])
		}
		return retObj
	}
}

It complains about

> invalid operation: cannot index A (variable of type T constrained by []interface{}|[][]interface{}|[][][]interface{}|[][][][]interface{})compilerNonIndexableOperand

Does anyone know how to do this job?

答案1

得分: 1

你不能使用泛型或者仅使用interface{}/any来实现这个功能。主要问题在于你无法静态地建模具有任意维度的切片,也就是任意类型的切片。让我们按顺序来解释:

你得到的错误信息是因为你不能使用类似那样的联合约束来索引类型参数。在规范中,索引表达式

> 对于类型参数类型 P 的 a:
>
> - [...]
> - P 的类型集中所有类型的元素类型必须相同。[...]

ArrayLike 的类型集的元素类型不相同[]interface{} 的元素类型是 interface{}[][]interface{} 的元素类型是 []interface{},以此类推。

有一个对索引错误的安慰性解决方案,在这里概述,基本上涉及将参数 AB 的类型更改为切片 []T。然后你可以索引 A[i]B[i]

**然而,这还不够。**此时,无法向递归调用传递正确的参数。表达式 A[i] 的类型现在是 T,但 subMatrix 需要的是 []T

即使放弃类型参数,并将参数 AB 声明为 any,也不起作用,因为在递归调用中仍然需要对它们进行索引。为了进行索引,你必须将 any 断言为可索引的类型,但是什么类型可以进行索引呢?在每次递归中,类型的维度会减少一个,因此静态类型的断言最终会引发 panic。

解决这个问题的唯一方法(可能是?)是使用反射,但是老实说,我不认为这样做有实际的用途。

你可以为每个矩阵维度编写一个通用函数,这是你应该做的:

import "golang.org/x/exp/constraints"

type Number interface {
    constraints.Integer | constraints.Float
}

func Sub2DMatrix[T Number](a, b [][]T) {
    for i := range a {
        for j := range a[i] {
            a[i][j] -= b[i][j]
        }
    }
}
英文:

You can't do this, not with generics and not with interface{}/any alone. The main issue is that you can't statically model slices with arbitrary dimensions — i.e. arbitrary types. Let's go in order:

The error message you got is because you can't index a type parameter with a union constraint like that. Specs, Index expressions:

> For a of type parameter type P:
>
> - [...]
> - The element types of all types in P's type set must be
> identical. [...]

The element types of ArrayLike's type set are not identical. The element type of []interface{} is interface{}, the one of [][]interface{} is []interface{} and so on.

There is a placebo solution to the indexing error, outlined here, basically it involves changing the type of the arguments A and B to a slice []T. Then you can index A[i] and B[i].

However this isn't enough. At this point, there's no way to pass correct arguments to the recursive call. The type of the expression A[i] is now T, but subMatrix wants []T.

Even dropping type parameters and declaring the args A and B as any doesn't work, because on the recursive call you still want to index them. In order to index, you have to assert any to something that's indexable, but what would that be? At each recursion the types would have one less dimension, so a statically typed assertion would eventually panic.

The only way to solve this is (probably?) with reflection, but honestly I don't see a practical utility to do that.

You can, and should, write a generic function for each matrix dimension:

import &quot;golang.org/x/exp/constraints&quot;

type Number interface {
    constraints.Integer | constraints.Float
}

func Sub2DMatrix[T Number](a, b [][]T) {
    for i := range a {
        for j := range a[i] {
            a[i][j] -= b[i][j]
        }
    }
}

huangapple
  • 本文由 发表于 2022年7月21日 09:30:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/73059728.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定