将列表或映射作为函数参数展开

2024-09-29 01:19:03 发布

您现在位置:Python中文网/ 问答频道 /正文

在Scala中,是否可以动态地将列表/元组/映射项作为函数的参数展开?我正在寻找与Python的args/kwargs等价的Scala。

例如,在Python中,如果一个函数定义为def foo(bar1, bar2, bar3=None, bar4=1),那么给定一个列表x=[1,7]和一个字典y={'bar3':True, 'bar4':9},您可以将foo调用为foo(*x, **y)


Tags: 函数列表参数定义foodefargs动态
3条回答

有点像OQ的边案例,但是如果你想为案例类传递一个参数映射,这似乎是可行的:

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)

(从Scala: How to access a class property dynamically by name?借来)

最初的答案并没有提到将Map作为对列表来处理,它可以很容易地转换为Map(even->;operator只是pair的缩写)。

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

为了清楚起见,以下是有效的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)

但是,没有类似的Scala语法。有一些类似的事情,但这永远不可能的主要原因是,它会违反Scala所要求的编译时类型检查。我们再仔细看看。

原因

首先,考虑varargs部分。在这里,您希望能够传入任意长度的参数列表,并让它填充相关的函数参数。这在Scala中是行不通的,因为类型检查器要求传递到函数中的参数是有效的。在您的场景中,foo()可以接受长度为2的参数列表,但不能小于2。但是,由于任何Seq都可以有任意数量的参数,类型检查器如何知道正在传递的x在编译时是有效的?

其次,想想keywword的论点。在这里,您要求函数接受参数和值的任意Map。但您也遇到了同样的问题:编译时类型检查器如何知道您正在传递所有必需的参数?或者,更进一步说,它们是正确的类型?毕竟,您给出的示例是一个同时包含布尔值和Int的映射,它将具有类型Map[String, Any],那么类型检查器如何知道这将匹配您的参数类型?

一些解决方案

斯卡拉变量

你可以做一些类似的事情,但不是这样。例如,如果将函数定义为显式使用varargs,则可以传入一个Seq:

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

这是因为Scala知道它只需要一个包含零个或多个参数的序列,而Seq总是包含零个或多个项,所以它匹配。此外,只有当类型也匹配时,它才起作用;在这里,它需要一个int序列,并得到它。

tupled

另一种方法是传入参数的元组:

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)

同样,这是因为Scala的类型检查器可以验证参数是否有效。该函数需要四个参数,类型为Int、Int、Boolean和Int,由于Scala中的元组具有固定的长度,并且每个位置都有已知(可能不同)的类型,因此类型检查器可以验证参数是否与预期参数匹配。

相关问题 更多 >