gpt4 book ai didi

scala - 具有重复参数的磁铁图案(可变参数)

转载 作者:行者123 更新时间:2023-12-04 12:41:41 25 4
gpt4 key购买 nike

是否可以使用 magnet pattern使用可变参数:

object Values {
implicit def fromInt (x : Int ) = Values()
implicit def fromInts(xs: Int*) = Values()
}
case class Values()

object Foo {
def bar(values: Values) {}
}

Foo.bar(0)
Foo.bar(1,2,3) // ! "error: too many arguments for method bar: (values: Values)Unit"

?

最佳答案

正如 gourlaysama 已经提到的,将可变参数变成单个 Product从语法上讲,会成功的:

implicit def fromInts(t: Product) = Values()

这允许以下调用编译良好:
Foo.bar(1,2,3)

这是因为编译器自动将 3 个参数提升为 Tuple3[Int, Int, Int] .这将适用于最多 22 个参数的任意数量的参数。现在的问题是如何使其类型安全。因为它是 Product.productIterator是在方法体中取回参数列表的唯一方法,但它返回 Iterator[Any] .我们不保证该方法只会被 Int 调用。 s。这应该不足为奇,因为我们实际上甚至从未在签名中提到我们只想要 Int s。

好的,所以不受约束的 Product 之间的关键区别可变参数列表是在后一种情况下,每个元素都属于同一类型。我们可以使用类型类对其进行编码:
abstract sealed class IsVarArgsOf[P, E]
object IsVarArgsOf {
implicit def Tuple2[E]: IsVarArgsOf[(E, E), E] = null
implicit def Tuple3[E]: IsVarArgsOf[(E, E, E), E] = null
implicit def Tuple4[E]: IsVarArgsOf[(E, E, E, E), E] = null
implicit def Tuple5[E]: IsVarArgsOf[(E, E, E, E, E), E] = null
implicit def Tuple6[E]: IsVarArgsOf[(E, E, E, E, E), E] = null
// ... and so on... yes this is verbose, but can be done once for all
}

implicit class RichProduct[P]( val product: P ) {
def args[E]( implicit evidence: P IsVarArgsOf E ): Iterator[E] = {
// NOTE: by construction, those casts are safe and cannot fail
product.asInstanceOf[Product].productIterator.asInstanceOf[Iterator[E]]
}
}

case class Values( xs: Seq[Int] )
object Values {
implicit def fromInt( x : Int ) = Values( Seq( x ) )
implicit def fromInts[P]( xs: P )( implicit evidence: P IsVarArgsOf Int ) = Values( xs.args.toSeq )
}


object Foo {
def bar(values: Values) {}
}

Foo.bar(0)
Foo.bar(1,2,3)

我们更改了方法签名形式
implicit def fromInts(t: Product)

到:
implicit def fromInts[P]( xs: P )( implicit evidence: P IsVarArgsOf Int )

在方法体内,我们使用新的方法 args获取我们的 arg 列表。

请注意,如果我们尝试调用 bar一个元组不是 Int 的元组s,我们会得到一个编译错误,这让我们恢复了类型安全。

更新 :正如 0__ 所指出的,我的上述解决方案不能很好地用于数值扩展。换句话说,以下内容无法编译,尽管 bar只是拿了 3 Int参数:
Foo.bar(1:Short,2:Short,3:Short)
Foo.bar(1:Short,2:Byte,3:Int)

要解决这个问题,我们需要做的就是修改 IsVarArgsOf这样所有的隐式都允许
元组元素可转换为通用类型,而不是全部为同一类型:
abstract sealed class IsVarArgsOf[P, E]
object IsVarArgsOf {
implicit def Tuple2[E,X1<%E,X2<%E]: IsVarArgsOf[(X1, X2), E] = null
implicit def Tuple3[E,X1<%E,X2<%E,X3<%E]: IsVarArgsOf[(X1, X2, X3), E] = null
implicit def Tuple4[E,X1<%E,X2<%E,X3<%E,X4<%E]: IsVarArgsOf[(X1, X2, X3, X4), E] = null
// ... and so on ...
}

好吧,其实我撒谎了,我们还没有完成。因为我们现在接受不同类型的元素(只要它们可以转换为通用类型,我们就不能将它们转换为预期的类型(这会导致运行时转换错误),而是必须应用隐式转换。我们可以像这样重做它:
abstract sealed class IsVarArgsOf[P, E] {
def args( p: P ): Iterator[E]
}; object IsVarArgsOf {
implicit def Tuple2[E,X1<%E,X2<%E] = new IsVarArgsOf[(X1, X2), E]{
def args( p: (X1, X2) ) = Iterator[E](p._1, p._2)
}
implicit def Tuple3[E,X1<%E,X2<%E,X3<%E] = new IsVarArgsOf[(X1, X2, X3), E]{
def args( p: (X1, X2, X3) ) = Iterator[E](p._1, p._2, p._3)
}
implicit def Tuple4[E,X1<%E,X2<%E,X3<%E,X4<%E] = new IsVarArgsOf[(X1, X2, X3, X4), E]{
def args( p: (X1, X2, X3, X4) ) = Iterator[E](p._1, p._2, p._3, p._4)
}
// ... and so on ...
}
implicit class RichProduct[P]( val product: P ) {
def args[E]( implicit isVarArg: P IsVarArgsOf E ): Iterator[E] = {
isVarArg.args( product )
}
}

这解决了数值扩大的​​问题,当混合不相关的类型时我们仍然得到编译:
scala> Foo.bar(1,2,"three")
<console>:22: error: too many arguments for method bar: (values: Values)Unit
Foo.bar(1,2,"three")
^

关于scala - 具有重复参数的磁铁图案(可变参数),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16879175/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com