【问题标题】:Pass list as individual function parameters将列表作为单个函数参数传递
【发布时间】:2019-01-24 17:46:04
【问题描述】:

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

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

【问题讨论】:

  • 写成评论,因为它不是一个完整的答案,但可能会带您走上正确的道路:您可以将您的函数库化为Int => Int => Int => Int => Int,然后获取您的整数列表和保持(部分)通过foldLeft 将它们一一应用到函数中。这个问题详细说明了这一点:*.com/questions/7606587/… 如果你有一个元组而不是一个列表,你可以将它应用到f.tupled,但如果你有一个列表,那就忘了。

标签: scala


【解决方案1】:

我不认为您可以在标准 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 是否适合该函数。例如,参数列表可能具有不匹配数量的元素。但如果这是用于单元测试,你也可以抛出一个异常。

【讨论】:

    【解决方案2】:

    如果您无法修改 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 个相同类型参数的方法。

    坏消息是,您需要为每个必需的参数使用不同的翻译器,如果您使用太少的参数调用 call(),编译器将无法捕捉到它。

    【讨论】: