【问题标题】:Is it possible to describe a function of: (any-arity => 1-arity)?是否可以描述以下函数:(any-arity => 1-arity)?
【发布时间】:2013-07-25 19:18:07
【问题描述】:

我希望能够将一个函数传递给一个类,这样该函数总是返回一个布尔值,但参数的数量可以是可变的。

这实际上是我现在正在使用的:

/**
  * Template class for any SecureSocial SecuredAction.
  */
class SomeAction[T](
    isSuccessful: (SecuredRequest[AnyContent], T) => Boolean, 
    success: Result, 
    failure: Result) extends SecureSocial {

      def toReturn = SecuredAction(WithProvider("google")) {
        implicit request => if (isSuccessful(request)) success else failure
      }

}

我希望 isSuccessful 参数是一个函数,它至少接受一个 SecuredRequest[AnyContent] 类型的参数,然后是零个或多个参数。有时我会传入一个只需要request 对象的函数,有时我会传入一个需要request 对象和一些其他参数的函数。

seeing this answer 之后,我查看了currying,即the practice of transforming a multiple-argument function into a single-argument function that will return a function if any more arguments are needed。这听起来可以解决我的问题,但是在阅读了difference between partially applied functions and currying 之后,我开始不这么认为了?看起来仍然有一定数量的最终参数......

我想成功的柯里化应该是这样的:

class SomeAction[T](
    isSuccessful : ((SecuredRequest[AnyContent])(T) => Boolean) ,  // ERROR
    ... ) ... { ... }

这绝对不是正确的语法。

class SomeAction[T](
    isSuccessful : (SecuredRequest[AnyContent] => Boolean) , 
    ... ) ... { ... }

toReturn 中的isSuccessful 执行某些操作,以取消对传递的函数的curry。

可能会引导我回答的多个问题:

  • 是否有一些特殊的语法来描述我不知道的柯里化函数?
  • 或者我有一个类型为(SecuredRequest[AnyContent] => Boolean) 的函数,但在isSuccessful(request) 调用上做一些非currying?

感谢阅读;我想我只是在寻找简单的例子。

【问题讨论】:

  • 为什么不让您的来电者这样做?如果您要求函数SecuredRequest[AnyContent] => Boolean,调用者可以处理部分应用任何其他参数。反正你也不知道要传递什么,对吧?
  • 谢谢@Myserious Dan!是的,这解决了我的问题^.^

标签: scala currying


【解决方案1】:

如果你有 (A*)B 类型的方法 f,那么 eta-expanded f _ 就是 Seq[A] => B

f 内部,重复的参数无论如何都是Seq[A] 类型,所以有意义。

有一张著名的门票:

https://issues.scala-lang.org/browse/SI-4176

它以立即弃用的选项 -Yeta-expand-keeps-star 而闻名。

这是一个方法示例,该方法采用箭头左侧带有星号的函数并调用它。

因此,如果“任意数量”是指“任意数量的 Ts”,例如键入 T = (String, String),那么 varargs 就足够了。

object Test extends App {

  def f(g: (Int*) => Int) = g(1,2,3)

  def sum(is: Int*) = is.sum

  Console println f(sum _)

  def f2(g: (Int*) => Int, h: =>Seq[Int]): Int = g(h: _*)

  Console println f2(sum _, List(1,2,3))

  def f3(g: (Int*) => Int, is: Int*): Int = g(is: _*)

  Console println f3(sum _, 1,2,3)
}

【讨论】:

  • 我见过这个。我的意思不是“任何数量的Ts”。我的意思是任何数量的任何东西,无论是StringbooleanInt,...
  • @MeredithLeu 当你的答案弹出时我有点想通,这就是为什么那天晚上我懒得格式化我的答案,但感谢 petr-pudlak 这样做。
  • 用户会自动收到有关 cmets 问题的通知,其中包含他们的名字,afaik。无论如何,感谢您花时间回答我的问题。
【解决方案2】:

如果您的函数采用可变数量的参数,您可以这样做。这是必需的,因为他们事先不知道该操作将提供多少参数。正如其他人提到的,可变参数函数被翻译为最后一个参数是序列的函数。所以你可以这样做:

case class Action[T](f: (String, Seq[T]) => Boolean);

def testfn(s: String, args: Int*): Boolean = true;

new Action[Int](testfn _);

如果您需要您的函数表明它们的参数数量错误,您可以让它们返回 Option 并可能为各种类型添加一些辅助函数,例如

case class Action[T](f: (String, Seq[T]) => Option[Boolean]);

然后,如果给定的参数数量错误,您可以为不同的 arity 提供帮助,将“正常”函数转换为返回 None 的可变参数:

// for 1 argument functions:
def toVar[T](f: (String, T) => Boolean)(s: String, xs: Seq[T]): Option[Boolean] =
  Some(xs).collect({ case Seq(x1) => f(s, x1) });
// for 2 argument functions:
def toVar[T](f: (String, T, T) => Boolean)(s: String, xs: Seq[T]): Option[Boolean] =
  Some(xs).collect({ case Seq(x1, x2) => f(s, x1, x2) });

def testfn(s: String, x1: Int, x2: Int): Boolean =
  (x1 == x2);

new Action[Int](toVar(testfn _));

如果您为仅调用 toVar 和主构造函数的不同 arity 为 Action 创建专门的构造函数,您甚至可以避免使用 toVar

【讨论】:

  • 感谢您的更新,顺便说一句,请参阅对我的回答的评论。所以不允许我向我希望发送的消息发送消息。
【解决方案3】:

知道了。

/**
  * Template class for any SecureSocial SecuredAction that involves using user information.
  * @param isSuccessful: SecuredRequest[AnyContent] => Boolean
  */
class UserReqAction(
  isSuccessful: (SecuredRequest[AnyContent] => Boolean),
  success: Result, failure: Result) extends SecureSocial {
  def toReturn = SecuredAction(WithProvider("google")) {
    implicit request => if (isSuccessful(request)) success else failure
  }
}

/**
  * Template class for any SecureSocial SecuredAction that does not use user information.
  * @param isSuccessful: SecuredRequest[AnyContent] => Boolean
  */
class UserNotReqAction(isSuccessful: () => Boolean,
                       success: Result, failure: Result) extends SecureSocial {
  def toReturn = SecuredAction(WithProvider("google")) {
    implicit request => if (isSuccessful()) success else failure
  }
}

// not curried example
def makeNewThing = new UserReqAction(
  userOp.createThing,
  Ok(Json.toJson(Json.obj("success" -> true, "msg" -> "Thing successfully created"))),
  BadRequest("failed to create Thing")).toReturn

// curried example
def editThing(param1: _, param2: _, etc) = new UserReqAction(
  userOp.updateThing(param1, param2, etc), // this is the currying
  Ok(Json.toJson(Json.obj("success" -> true, "msg" -> "Thing successfully edited"))),
  BadRequest("failed to edit Thing")).toReturn

def updateThing(param1: _, param2: _, etc)(request: SecuredRequest[AnyContent] ) {...}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多