6

1つの引数を取る関数があるとしましょう

def fun(x: Int) = x

これに基づいて、同じ呼び出し規約で新しい関数を生成したいのですが、元の関数に委任する前に、引数に変換を適用します。そのために、私はできました

def wrap_fun(f: (Int) => Int) = (x: Int) => f(x * 2)
wrap_fun(fun)(2) // 4

変換を共通に適用するための引数の一部しか持たないアリティの関数を除いて、どのようにして同じことを行うことができますか?

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

4 に答える 4

6

Scalaでいつものように、やりたいことを達成するためのさらに別の方法があります。

これは、最初の引数とのカリー化に基づくテイクcomposeですFunction1

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

REPLが示すように、結果のタイプは次のようになります。

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オブジェクトになりました。これにより、次のような電話をかけることができます。

(fun1 _ compose modify)(2)(5)
(fun2 _ compose modify)(2)(Map(), Seq())

どちらも4を返します。確かに、のアプリケーションを関数オブジェクト自体(この場合はメソッドを呼び出したい)_と区別するためにを追加する必要があることを考えると、構文はそれほど良くありません。fun1compose

したがって、一般的に不可能であるというルイージの主張は依然として有効ですが、関数を自由にカレーすることができれば、この素晴らしい方法でそれを行うことができます。

于 2012-04-19T09:01:44.710 に答える
6

これは、関数のアリティを抽象化するためのshapelessの機能を使用して、かなり簡単に実行できます。

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

ここで、構文的に最初のアプリケーション(の暗黙の引数リストとともに)を2番目のアプリケーション(明示的な引数リストを持つ変換された関数の)からapply構文的にマークします。wrap_fun

于 2012-04-19T13:30:11.910 に答える
2

異なる数の引数を取る関数は異なる、無関係な型であるため、これを一般的に行うことはできません。trait Function1 [-T1, +R] extends AnyRef何もありません。アリティごとに個別のメソッドが必要になります。

于 2012-04-18T19:39:22.660 に答える
1

私が投票してルイージの答えに同意している間、あなたは知っています...彼は正しいです; Scalaに、そのようなことに対する直接的な組み込みのサポートはありません。あなたがやろうとしていることは不可能ではないことに注意する価値があります。それをやめるのは少し面倒なことです。多くの場合、目的のアリティごとに個別のメソッドを実装するのが最善です。

とはいえ、実際にこれを行うことができますHList。試してみたい場合は、当然、HList実装を入手する必要があります。MilesSabinの優れた形のないプロジェクトとその実装を利用することをお勧めし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
于 2012-04-18T20:16:17.970 に答える