如果我想定义一个带有一个或多个参数和一个可调用函数(一个函数)的函数,以及为什么注释是这样的,我想知道它是如何工作的。
我将以此answer为例:
// Returning T, throwing the exception on failure
@annotation.tailrec
final def retry[T](n: Int)(fn: => T): T = {
util.Try { fn } match {
case util.Success(x) => x
case _ if n > 1 => retry(n - 1)(fn)
case util.Failure(e) => throw e
}
}
在这个函数中有一些有趣的东西:
retry[T]
我的问题在第4点。为什么以及如何使用two
圆括号来定义此函数。如果要将可调用函数传递给任何函数,是否应始终使用可选参数“list”旁边的圆括号?为什么不把参数放在一起?
提前谢谢
答案 0 :(得分:1)
说实话,您不必使用多个参数列表来传递函数作为参数。 E.g。
document.body.appendChild(link);
link.click();
会完全奏效。但是,你怎么称呼它?
def pass(string: String, fn: String => Unit): Unit = fn(string)
有些人会发现它很笨拙。你宁愿想传递它:
pass("test", s => println(s))
甚至
pass("test")(s => println(s))
使其看起来好像函数是附加到pass("test") { s =>
println(s)
}
的一个块,该块用一个参数调用。
使用单个参数列表,您将被强制写入:
pass
使用最后一个参数curry,您只需获得更舒适的语法。在像Haskell这样的语言中,curry会自动发生,你不必为像这样的句法设计决定而烦恼。不幸的是,Scala要求您明确做出这些决定。
答案 1 :(得分:1)
您可以在函数声明中包含多个参数列表。它与将所有参数合并到一个列表(def foo(a: Int)(b: Int)
或多或少等同于def foo(a: Int, b: Int)
)大致相同,但有一些区别:
您可以在声明中引用先前列表中的参数:def foo(a : Int, b: Int = a + 1)
不起作用,但def foo(a: Int)(b: Int = a +1)
不起作用。
可以在参数列表之间推断类型参数:def foo[T](x: T, f: T => String): String ; foo(1, _.toString)
不起作用(您必须编写foo[Int](1, _.toString)
,但def foo[T](x: T)(f: T => String); foo(1)(_.toString)
会这样做。< / p>
您只能将整个列表声明为隐式,因此,当您需要隐式某些参数而不是其他参数时,多个列表会很有用:def foo(a: Int)(implicit b: Configuration)
然后,有一些语法上的优势 - 你可以用单个列表做的事情,但他们只是看起来更丑陋:
柯里:
def foo(a: Int)(b: Int): String
val bar: Int => String = foo(1)
你也可以用单个列表写这个,但它看起来不太好看:
def foo(a: Int, b: Int): String
val bar: Int => String = foo(1, _)
最后,问你的问题:
def retry[T](n: Int)(f: => T)
很好,因为只有一个参数的列表周围的括号是可选的。所以,这可以让你写出像
这样的东西retry(3) {
val c = createConnection
doStuff(c)
closeConnection(c)
}
如果将f
合并到同一个列表中,那么看起来会更加丑陋。
另外,currying很有用:
val transformer = retry[Int](3)
Seq(1,2,3).map { n => transformer(n + 1) }
Seq(4,5,6).map { n => transformer(n * 2) }
答案 2 :(得分:0)
如果你想将可调用函数传递给任何函数,你应该这样做 总是在&#34;列表旁边使用圆括号&#34;可选参数? 为什么不把参数放在一起?
没有这样的义务,它(大部分)是风格的问题。 IMO,它也导致更清晰的语法。使用两个参数列表,其中第二个是产生结果的函数,我们可以调用retry
方法:
val res: Try[Int] retry(3) {
42
}
相反,我们使用单个参数列表,我们的方法调用如下所示:
val res: Try[Int] = retry(3, () => {
42
})
我找到第一个语法清理器,它还允许您在仅提供第一个参数列表时使用retry
作为curried方法
但是,如果我们考虑更高级的用例,Scala类型推断在参数列表之间,而不是在它们内部。这意味着如果我们有一个方法:
def mapFun[T, U](xs: List[T])(f: T => U): List[U] = ???
我们可以调用我们的方法,而无需在呼叫站点指定T
或U
的类型:
val res: List[Int] = mapFun(List.empty[Int])(i => i + 1)
但是如果我们使用单个参数列表,
def mapFun2[T, U](xs: List[T], f: T => U): List[U] = ???
这不会编译:
val res2 = mapFun2(List.empty[Int], i => i + 1)
相反,我们需要写:
val res2 = mapFun2[Int, Int](List.empty[Int], i => i + 1)
帮助编译器选择正确的类型。