简体   繁体   English

在Scala中将列表或映射展开为函数参数

[英]Unwrapping a list or map as function arguments in Scala

Is it possible to dynamically unwrap a list/tuple/map items as arguments to a function in Scala? 是否可以动态解包列表/元组/映射项作为Scala中函数的参数? I am looking for a Scala equivalent of Python's args / kwargs . 我正在寻找与Python的args / kwargs等效的Scala。

For instance, in Python if a function is defined as def foo(bar1, bar2, bar3=None, bar4=1) then given a list x=[1,7] and a dictionary y={'bar3':True, 'bar4':9} you can call foo as foo(*x, **y) . 例如,在Python中,如果函数定义为def foo(bar1, bar2, bar3=None, bar4=1)则给出列表x=[1,7]和字典y={'bar3':True, 'bar4':9}你可以把foo称为foo(*x, **y)

Just to be clear, the following is valid Python code: 为了清楚起见,以下是有效的Python代码:

def foo(bar1, bar2, bar3=None, bar4=1): print("bar1="+str(bar1)+" bar2="+str(bar2)+" bar3="+str(bar3)+" bar4="+str(bar4))
x=[1,7]
y={'bar3':True, 'bar4':9}
foo(*x,**y)

However, there is no analogous Scala syntax. 但是,没有类似的Scala语法。 There are some similar things, but the main reason this is never going to be possible is that it would violate the compile-time type checking that Scala requires. 有一些类似的东西,但这是永远不可能的主要原因是它会违反Scala所需的编译时类型检查。 Let's look more closely. 让我们仔细看看。

The reasons 原因

First, think about the varargs portion. 首先,考虑一下varargs部分。 Here you want to be able to pass in an arbitrary-length list of arguments and have it fill in the relevant function parameters. 在这里,您希望能够传入任意长度的参数列表,并填写相关的函数参数。 This will never work in Scala because the type checker requires that the parameters passed into a function be valid. 这在Scala中永远不会起作用,因为类型检查器要求传递给函数的参数有效。 In your scenario, foo() can accept a parameter list x of length two, but no less. 在您的场景中, foo()可以接受长度为2但不小于的参数列表x But since any Seq can have an arbitrary number of parameters, how would the type checker know that the x being pass it is valid at compile time? 但是因为任何Seq都可以有任意数量的参数,所以类型检查器如何知道传递的x在编译时是有效的?

Second, think about the keywword arguments. 其次,考虑关键词论点。 Here you are asking for the function to accept an arbitrary Map of arguments and values. 在这里,您要求函数接受任意参数和值的Map But you get the same problem: How can the compile-time type checker know that you are passing in all of the necessary arguments? 但是你得到了同样的问题:编译时类型检查器如何知道你传递了所有必要的参数? Or, further, that they are the right types? 或者,进一步说,他们是正确的类型? After all, they example you give is a Map containing both a Boolean and an Int, which would have the type Map[String, Any] , so how would the type checker know that this would match your parameter types? 毕竟,他们给出的示例是一个包含布尔值和Int的Map[String, Any] ,它的类型为Map[String, Any] ,那么类型检查器如何知道这将匹配您的参数类型?

Some solutions 一些解决方案

Scala's varargs Scala的varargs

You can do some similar things, but not this exactly. 你可以做一些类似的事情,但不是这个。 For example, if you defined your function to explicitly use varargs, you can pass in a Seq: 例如,如果您将函数定义为显式使用varargs,则可以传入Seq:

def foo(bar1: Int*) = println(f"bar1=$bar1")
val x = Seq(1, 2)
foo(x:_*)

This works because Scala knows that it only needs a sequence of zero or more arguments, and a Seq will always contain zero or more items, so it matches. 这是有效的,因为Scala知道它只需要一个零个或多个参数的序列,而Seq将始终包含零个或多个项目,因此它匹配。 Further, it only works if the types match as well; 此外,它只适用于类型匹配的情况; here it's expecting a sequence of Ints, and gets it. 在这里它期待一系列的Ints,并得到它。

tupled

The other thing you can do is to pass in a tuple of arguments: 你可以做的另一件事是传入一个参数元组

def foo(bar1: Int, bar2: Int, bar3: Boolean = false, bar4: Int = 1) = println(f"bar1=$bar1 bar2=$bar2 bar3=$bar3 bar4=$bar4")
val x = (1, 2, true, 9)
(foo _).tupled(x)

Again, this works because Scala's type checker can verify that the arguments are valid. 同样,这是有效的,因为Scala的类型检查器可以验证参数是否有效。 The function requires four arguments, of types Int, Int, Boolean, and Int, and since a tuple in Scala has a fixed length and known (and possibly different) types for each position, the type-checker can verify that the arguments match the expected parameters. 该函数需要四个参数,类型为Int,Int,Boolean和Int,并且由于Scala中的元组具有固定长度和每个位置的已知(可能不同)类型,因此类型检查器可以验证参数是否与预期参数。

原始答案没有提到处理Map作为对列表 - 它可以很容易地转换为map(偶数 - >运算符只是对的简写)。

def parse(options: (String, String)*) = println (options.toMap)

Sort of an edge case for the OQ but if you want to pass a Map of arguments for a case class this seems to work: OQ的边缘情况的排序,但如果你想传递一个案例类的参数Map,这似乎有效:

scala> case class myCC(foo: String = "bar", negInt: Int = -1)

scala> val row = myCC()
scala> println(row)
myCC(bar,-1)

scala> val overrides = Map("foo" -> "baz")
scala> row.getClass.getDeclaredFields foreach { f =>
 f.setAccessible(true)
 overrides.foreach{case (k,v) => if (k == f.getName) f.set(row, v)}
}
scala> println(row)
myCC(baz,-1)

(borrowed from Scala: How to access a class property dynamically by name? ) (借用Scala:如何通过名称动态访问类属性?

You can use varargs syntax: 您可以使用varargs语法:

def printAll(strings: String*) {
  strings.map(println)
}

No you can use this function so: 不,你可以使用这个功能:

printAll("foo")

so: 所以:

printAll("foo", "bar")

or so: 或者:

printAll("foo", "bar", "baz")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM