Scala函数转换

时间:2012-04-18 19:11:15

标签: scala functional-programming closures

说我有一个函数采用一个参数

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定义如何工作?

4 个答案:

答案 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