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

时间:2013-12-22 15:42:06

标签: python scala

是否可以动态解包list / tuple / map项作为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)

4 个答案:

答案 0 :(得分:11)

为了清楚起见,以下是有效的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但不小于的参数列表x。但是因为任何Seq都可以有任意数量的参数,所以类型检查器如何知道传递它的x在编译时是有效的?

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

一些解决方案

Scala的varargs

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

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

这是有效的,因为Scala知道它只需要一个零个或多个参数的序列,而Seq将始终包含零个或多个项目,因此它匹配。此外,它只适用于类型匹配的情况;在这里它期待一系列的Ints,然后得到它。

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中的元组具有固定长度和每个位置的已知(可能不同)类型,因此类型检查器可以验证参数是否与预期参数。

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

OQ的边缘大小写的排序但是如果你想传递一个case类的参数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)

(借鉴Scala: How to access a class property dynamically by name?

答案 3 :(得分:0)

您可以使用varargs语法:

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

不,你可以这样使用这个功能:

printAll("foo")

所以:

printAll("foo", "bar")

左右:

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