说我有一个函数采用一个参数
def fun(x: Int) = x
基于此,我想生成一个具有相同调用约定的新函数,但是在委托给原始函数之前,它将对其参数应用一些转换。为此,我可以
def wrap_fun(f: (Int) => Int) = (x: Int) => f(x * 2)
wrap_fun(fun)(2) // 4
一个人怎么可能做同样的事情,除了任何arity的函数只有参数的一部分来共同应用转换?
def fun1(x: Int, y: Int) = x
def fun2(x: Int, foo: Map[Int,Str], bar: Seq[Seq[Int]]) = x
wrap_fun(fun1)(2, 4) // 4
wrap_fun(fun2)(2, Map(), Seq()) // 4
上述调用的wrap_fun
定义如何工作?
答案 0 :(得分:6)
与Scala一样,还有另一种方法可以达到你想要做的目的。
以下是基于第一个参数与compose
的{{1}}一起讨论的内容:
Function1
作为REPL的结果类型显示您将:
def fun1(x : Int)(y : Int) = x
def fun2(x : Int)(foo : Map[Int, String], bar : Seq[Seq[Int]]) = x
def modify(x : Int) = 2*x
而不是包含函数fun1: (x: Int)(y: Int)Int
fun2: (x: Int)(foo: Map[Int,String], bar: Seq[Seq[Int]])Int
modify: (x: Int)Int
和fun1
,而是fun2
它们,从技术上讲,它们现在都是compose
个对象。这允许您拨打以下电话:
Function1
两者都将返回4.当然,语法不是很好,因为你必须添加(fun1 _ compose modify)(2)(5)
(fun2 _ compose modify)(2)(Map(), Seq())
来区分_
的应用程序与函数对象本身(你在其上)想要在这种情况下调用fun1
方法。
所以Luigi认为一般不可能的论点仍然有效,但如果你可以自由地讨论你的功能,你可以用这种方式做到这一点。
答案 1 :(得分:6)
这可以通过shapeless's工具相当直接地完成,以便在函数arity上进行抽象,
import shapeless._
import HList._
import Functions._
def wrap_fun[F, T <: HList, R](f : F)
(implicit
hl : FnHListerAux[F, (Int :: T) => R],
unhl : FnUnHListerAux[(Int :: T) => R, F]) =
((x : Int :: T) => f.hlisted(x.head*2 :: x.tail)).unhlisted
val f1 = wrap_fun(fun _)
val f2 = wrap_fun(fun1 _)
val f3 = wrap_fun(fun2 _)
示例REPL会话,
scala> f1(2)
res0: Int = 4
scala> f2(2, 4)
res1: Int = 4
scala> f3(2, Map(), Seq())
res2: Int = 4
请注意,您不能立即应用包装函数(如在问题中)而不是通过指定的val(如上所述),因为包装函数的显式参数列表将与隐式参数混淆wrap_fun
列表。我们可以在问题中找到最接近表单的方法是明确命名apply
方法,如下所示
scala> wrap_fun(fun _).apply(2)
res3: Int = 4
scala> wrap_fun(fun1 _).apply(2, 4)
res4: Int = 4
scala> wrap_fun(fun2 _).apply(2, Map(), Seq())
res5: Int = 4
这里明确提到apply
从第二个应用程序(带有显式参数列表的转换函数)中语法标记第一个应用程序(wrap_fun
及其隐式参数列表)。 / p>
答案 2 :(得分:2)
由于使用不同数量的参数的函数是不同的,不相关的类型,因此通常不能这样做。 trait Function1 [-T1, +R] extends AnyRef
而没有别的。每个arity都需要一个单独的方法。
答案 3 :(得分:1)
虽然我投票赞成并同意Luigi的答案 - 因为,你知道......他正确; Scala 没有对这样的东西有直接的内置支持 - 值得注意的是你要做的事情不是不可能;这只是一个很痛苦的事情,而且,通常情况下,你最好只是按照所希望的方式实施一个单独的方法。
尽管如此......我们实际上可以这样做HList
。如果您有兴趣尝试,那么您自然需要获得HList
实施。我建议使用Miles Sabin出色的shapeless项目及其HList
的实现。无论如何,这里有一个使用它的例子来完成类似于你似乎在寻找的东西:
import shapeless._
trait WrapperFunner[T] {
type Inputs <: HList
def wrapFun(inputs: Inputs) : T
}
class WrapsOne extends WrapperFunner[Int] {
type Inputs = Int :: HNil
def wrapFun(inputs: Inputs) : Int = {
inputs match {
case num :: HNil => num * 2
}
}
}
class WrapsThree extends WrapperFunner[String] {
type Inputs = Int :: Int :: String :: HNil
def wrapFun(inputs: Inputs) : String = {
inputs match {
case firstNum :: secondNum :: str :: HNil => str + (firstNum - secondNum)
}
}
}
object MyApp extends App {
val wo = new WrapsOne
println(wo.wrapFun(1 :: HNil))
println(wo.wrapFun(17 :: HNil))
//println(wo.wrapFun(18 :: 13 :: HNil)) // Would give type error
val wt = new WrapsThree
println(wt.wrapFun(5 :: 1 :: "your result is: " :: HNil))
val (first, second) = (60, 50)
println(wt.wrapFun(first :: second :: "%s minus %s is: ".format(first, second) :: HNil))
//println(wt.wrapFun(1 :: HNil)) // Would give type error
}
运行MyApp
会导致:
2
34
your result is: 4
60 minus 50 is: 10
或者,更接近您的特定情况:
import shapeless._
trait WrapperFunner[T] {
type Inputs <: HList
def wrapFun(inputs: Inputs) : T
}
trait WrapperFunnerBase extends WrapperFunner[Int] {
// Does not override `Inputs`
def wrapFun(inputs: Inputs) : Int = {
inputs match {
case (num: Int) :: remainder => num
}
}
}
class IgnoresNothing extends WrapperFunnerBase {
type Inputs = Int :: HNil
}
class IgnoresLastTwo extends WrapperFunnerBase {
type Inputs = Int :: Int :: String :: HNil
}
object MyApp extends App {
val in = new IgnoresNothing
println(in.wrapFun(1 :: HNil))
println(in.wrapFun(2 :: HNil))
//println(in.wrapFun(3 :: 4 :: HNil)) // Would give type error
val ilt = new IgnoresLastTwo
println(ilt.wrapFun(60 :: 13 :: "stupid string" :: HNil))
println(ilt.wrapFun(43 :: 7 :: "man, that string was stupid..." :: HNil))
//println(ilt.wrapFun(1 :: HNil)) // Would give type error
}
结果:
1
2
60
43