在java中编程时,我总是记录输入参数和方法的返回值,但在scala中,方法的最后一行是返回值.所以我必须这样做:
def myFunc() = {
val rs = calcSomeResult()
logger.info("result is:" + rs)
rs
}
Run Code Online (Sandbox Code Playgroud)
为了方便起见,我写了一个实用工具:
class LogUtil(val f: (String) => Unit) {
def logWithValue[T](msg: String, value: T): T = { f(msg); value }
}
object LogUtil {
def withValue[T](f: String => Unit): ((String, T) => T) = new LogUtil(f).logWithValue _
}
Run Code Online (Sandbox Code Playgroud)
然后我用它作为:
val rs = calcSomeResult()
withValue(logger.info)("result is:" + rs, rs)
Run Code Online (Sandbox Code Playgroud)
它会记录该值并将其返回.它对我有用,但似乎很奇怪.因为我是一个古老的java程序员,但对scala不熟悉,我不知道在scala中是否有更惯用的方法.
感谢您的帮助,现在我使用romusz提到的Kestrel组合器创建了一个更好的工具
object LogUtil {
def kestrel[A](x: A)(f: A => Unit): A = { f(x); x }
def logV[A](f: String => Unit)(s: String, x: A) = kestrel(x) { y => f(s + ": " + y)}
}
Run Code Online (Sandbox Code Playgroud)
我添加f参数,以便我可以从slf4j传递一个记录器,测试用例是:
class LogUtilSpec extends FlatSpec with ShouldMatchers {
val logger = LoggerFactory.getLogger(this.getClass())
import LogUtil._
"LogUtil" should "print log info and keep the value, and the calc for value should only be called once" in {
def calcValue = { println("calcValue"); 100 } // to confirm it's called only once
val v = logV(logger.info)("result is", calcValue)
v should be === 100
}
}
Run Code Online (Sandbox Code Playgroud)
rom*_*usz 34
您要查找的是所谓的红隼组合子(K组合子)Kxy = x.您可以在返回传递给它的值时执行各种副作用操作(不仅仅是记录).阅读https://github.com/raganwald/homoiconic/blob/master/2008-10-29/kestrel.markdown#readme
在Scala中,实现它的最简单方法是:
def kestrel[A](x: A)(f: A => Unit): A = { f(x); x }
Run Code Online (Sandbox Code Playgroud)
然后,您可以将打印/日志记录功能定义为:
def logging[A](x: A) = kestrel(x)(println)
def logging[A](s: String, x: A) = kestrel(x){ y => println(s + ": " + y) }
Run Code Online (Sandbox Code Playgroud)
并使用它像:
logging(1 + 2) + logging(3 + 4)
Run Code Online (Sandbox Code Playgroud)
你的示例函数变成了一个单行:
def myFunc() = logging("result is", calcSomeResult())
Run Code Online (Sandbox Code Playgroud)
如果您更喜欢OO表示法,您可以使用其他答案中显示的含义,但这种方法的问题在于,每次要记录某些内容时都会创建一个新对象,如果经常这样做,可能会导致性能下降.但为了完整性,它看起来像这样:
implicit def anyToLogging[A](a: A) = new {
def log = logging(a)
def log(msg: String) = logging(msg, a)
}
Run Code Online (Sandbox Code Playgroud)
使用它像:
def myFunc() = calcSomeResult().log("result is")
Run Code Online (Sandbox Code Playgroud)
如果您更喜欢更通用的方法,可以定义
implicit def idToSideEffect[A](a: A) = new {
def withSideEffect(fun: A => Unit): A = { fun(a); a }
def |!>(fun: A => Unit): A = withSideEffect(fun) // forward pipe-like
def tap(fun: A => Unit): A = withSideEffect(fun) // public demand & ruby standard
}
Run Code Online (Sandbox Code Playgroud)
并使用它
calcSomeResult() |!> { rs => logger.info("result is:" + rs) }
calcSomeResult() tap println
Run Code Online (Sandbox Code Playgroud)
你有基本的想法 - 你需要整理一下才能使它最方便.
class GenericLogger[A](a: A) {
def log(logger: String => Unit)(str: A => String): A = { logger(str(a)); a }
}
implicit def anything_can_log[A](a: A) = new GenericLogger(a)
Run Code Online (Sandbox Code Playgroud)
现在你可以
scala> (47+92).log(println)("The answer is " + _)
The answer is 139
res0: Int = 139
Run Code Online (Sandbox Code Playgroud)
这样你就不需要重复自己(例如没有rs两次).
从 开始Scala 2.13,链接操作tap可用于在返回原始值的同时对任何值应用副作用(在本例中为一些日志记录):
def tap[U](f: (A) => U): A
例如:
scala> val a = 42.tap(println)
42
a: Int = 42
Run Code Online (Sandbox Code Playgroud)
或者在我们的例子中:
import scala.util.chaining._
def myFunc() = calcSomeResult().tap(x => logger.info(s"result is: $x"))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2933 次 |
| 最近记录: |