通过列表作为单独的功能参数

时间:2019-01-24 09:00:48

标签: scala

我有一个函数def f(a: Int, b: Int, c: Int, d: Int, ...),我想提供一个连续整数列表作为参数(在单元测试中)。

是否有一种为(1 to N).toList提供f的整洁方法?由于函数不是def f(x: Int*,所以我不能简单地将list: _*与整数列表list一起使用。

2 个答案:

答案 0 :(得分:2)

我认为您不能以类型安全的方式在标准Scala中进行此操作,但是可以使用Shapeless库进行此操作。即使您不在项目中使用此库,也可以仅将其用于测试范围。

这是代码示例:

import shapeless._
import shapeless.ops.function._
import shapeless.ops.traversable._

def applyToList[F, HL <: HList, R](f: F)(args: List[Any])(implicit
  // convert `(T1, T2, ...) => R` to a function from a single HList argument `HL => R`
  fnToProduct: FnToProduct.Aux[F, HL => R],  
  // convert the argument list to the HList, expected by the converted function
  toHList: FromTraversable[HL]
): R = {
  toHList(args) match {
    case Some(hargs) => fnToProduct(f)(hargs)
    case None => sys.error("argument list not applicable to function")
  }
}

使用非常简单:

def f(a: Int, b: Int, c: Int, d: Int): Any = ???
applyToList(f _)(List(1, 2, 3, 4))

但是请注意,需要显式的eta转换f _,因为Scala编译器并不真正知道F是一个函数。

您可以更改定义以返回Option[R],因为在编译时无法知道所提供的List是否适合该函数。例如,参数列表可能具有不匹配数量的元素。但是,如果这是用于单元测试,则最好抛出一个异常。

答案 1 :(得分:1)

如果您无法修改f(),则说明您有点卡住了。您可以做的一件事(非同凡响)是写一个隐式翻译器。

让我们假装f()占用4个Int参数。

implicit class Caller4[A,R](func: (A,A,A,A)=>R) {
  def call(as :A*) :R = func(as(0),as(1),as(2),as(3))
}

(f _).call(1 to 4:_*)    //call f(1,2,3,4)

一个很好的新功能是,它将对采用4个相同类型参数的任何/所有方法都有效。

一个坏消息是,您需要为每个必需的arity使用不同的转换器,并且如果您使用太少的参数调用call(),编译器将无法捕获它。