[SCALA] 스칼라 로그인 할 때 어떻게 반환 값을 유지
SCALA스칼라 로그인 할 때 어떻게 반환 값을 유지
자바 프로그래밍 할 때, 나는 항상 입력 매개 변수를 기록하고 방법의 값을 반환하지만, 스칼라, 방법의 마지막 줄은 리턴 값이다. 그래서 내가 좋아하는 뭔가를해야 :
def myFunc() = {
val rs = calcSomeResult()
logger.info("result is:" + rs)
rs
}
쉽게 만들기 위해, 나는 유틸리티를 쓰기 :
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 _
}
그리고 나는 그것을 사용 :
val rs = calcSomeResult()
withValue(logger.info)("result is:" + rs, rs)
이 값을 기록하고 반환합니다. 그것은 나를 위해 작동하지만 이상한 것 같다. 나는 오래된 자바 프로그래머,하지만 스칼라 새로운 오전, 나는 스칼라에서이 작업을 수행 할 수있는 더 관용적 방법이 있는지 모른다.
덕분에 당신의 도움을 위해, 지금은 더 나은 황조롱이 연결자를 사용 util을 romusz에 의해 metioned 만들
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)}
}
I는 그것을 SLF4J에서 로거를 통과 할 수 있도록 I는 F 매개 변수를 추가하고, 테스트 케이스이다 :
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
}
}
해결법
-
==============================
1.Kxy의 =의 X : 당신이 찾고있는 황조롱이의 콤비 (K 연결자)라고합니다. 당신은 전달 된 값을 반환하면서 부작용 작업 (뿐만 아니라 로깅) 모든 종류의 작업을 수행 할 수 있습니다. https://github.com/raganwald/homoiconic/blob/master/2008-10-29/kestrel.markdown#readme 읽기
Kxy의 =의 X : 당신이 찾고있는 황조롱이의 콤비 (K 연결자)라고합니다. 당신은 전달 된 값을 반환하면서 부작용 작업 (뿐만 아니라 로깅) 모든 종류의 작업을 수행 할 수 있습니다. https://github.com/raganwald/homoiconic/blob/master/2008-10-29/kestrel.markdown#readme 읽기
스칼라에서 그것을 구현하는 가장 간단한 방법은 다음과 같습니다
def kestrel[A](x: A)(f: A => Unit): A = { f(x); x }
그런 다음 당신은 당신의 인쇄 / 로깅 기능으로 정의 할 수 있습니다 :
def logging[A](x: A) = kestrel(x)(println) def logging[A](s: String, x: A) = kestrel(x){ y => println(s + ": " + y) }
그리고 그것을처럼 사용
logging(1 + 2) + logging(3 + 4)
귀하의 예제 함수는 한 줄이된다 :
def myFunc() = logging("result is", calcSomeResult())
당신은 다른 답변에서와 같이 OO 표기법 당신이 implicits 사용할 수 있습니다 선호하지만, 이러한 접근 방식의 문제는 새 객체에게 당신이 그것을 자주 할 경우 성능이 저하 될 수 있습니다 무언가를 기록 할 때마다 만들거야 경우. 그러나 완성도를 들어, 다음과 같습니다 :
implicit def anyToLogging[A](a: A) = new { def log = logging(a) def log(msg: String) = logging(msg, a) }
를 같이 사용합니다 :
def myFunc() = calcSomeResult().log("result is")
-
==============================
2.당신은 더 나은 더 일반적인 접근 방식을 좋아한다면, 당신은 정의 할 수 있습니다
당신은 더 나은 더 일반적인 접근 방식을 좋아한다면, 당신은 정의 할 수 있습니다
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 }
와 같이 사용
calcSomeResult() |!> { rs => logger.info("result is:" + rs) } calcSomeResult() tap println
-
==============================
3.당신은 기본 아이디어 권리가 있습니다 - 당신은 그냥 최대한 편안함을 위해 조금을 정돈 할 필요가있다.
당신은 기본 아이디어 권리가 있습니다 - 당신은 그냥 최대한 편안함을 위해 조금을 정돈 할 필요가있다.
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)
지금 당신은 할 수
scala> (47+92).log(println)("The answer is " + _) The answer is 139 res0: Int = 139
당신이 자신을 반복 할 필요가 없습니다이 방법 (예를 들어, 어떤 RS 회).
-
==============================
4.의 이미 모든 당신 로거에 대한 기본 클래스가 있다고 가정 해 봅시다 :
의 이미 모든 당신 로거에 대한 기본 클래스가 있다고 가정 해 봅시다 :
abstract class Logger { def info(msg:String):Unit }
그런 다음 @@ 로깅 방법과 문자열을 확장 할 수 있습니다 :
object ExpressionLog { // default logger implicit val logger = new Logger { def info(s:String) {println(s)} } // adding @@ method to all String objects implicit def stringToLog (msg: String) (implicit logger: Logger) = new { def @@ [T] (exp: T) = { logger.info(msg + " = " + exp) exp } } }
로깅을 사용하려면 ExpressionLog 개체의 멤버를 가져해야 할 것이다 그리고 당신은 쉽게 다음과 같은 표기법을 사용하여 식을 로그온 할 수 :
import ExpressionLog._ def sum (a:Int, b:Int) = "sum result" @@ (a+b) val c = sum("a" @@ 1, "b" @@2)
인쇄합니다 :
이렇게하면 문자열 컴파일러에 @@ 메서드를 호출 할 때마다 문자열이 방법이 없음을 실현하기 때문에 작동하고 자동으로 정의 @@ 방법 (stringToLog 참조)가 익명의 유형 객체로 변환합니다. 변환 컴파일러의 일부가 암시 적 매개 변수로 원하는 로거를 선택합니다,이 방법은 아직 당신이 로거는 모든 시간을 사용할 필요가있는 완전한 제어권을 유지하려면 @@ 모든 시간 로거에 전달 유지할 필요가 없습니다.
@@ 방법은 중위 표기법을 사용하는 경우 지금까지 우선 순위가 간다 그것은 쉽게 기록됩니다 것을 추론 할 수있게 가장 높은 우선 순위를 가지고있다.
그래서 당신은 당신의 방법 중 하나에서 다른 로거를 사용하는 것을 원한다면? 이것은 매우 간단하다 :
import ExpressionLog.{logger=>_,_} // import everything but default logger // define specific local logger // this can be as simple as: implicit val logger = new MyLogger implicit val logger = new Logger { var lineno = 1 def info(s:String) { println("%03d".format(lineno) + ": " + s) lineno+=1 } } // start logging def sum (a:Int, b:Int) = a+b val c = "sum result" @@ sum("a" @@ 1, "b" @@2)
윌 출력 :
-
==============================
5.모든 해답, 장점과 단점을 컴파일, 나는 (상황이 재생 응용 프로그램)이 함께했다 :
모든 해답, 장점과 단점을 컴파일, 나는 (상황이 재생 응용 프로그램)이 함께했다 :
import play.api.LoggerLike object LogUtils { implicit class LogAny2[T](val value : T) extends AnyVal { def @@(str : String)(implicit logger : LoggerLike) : T = { logger.debug(str); value } def @@(f : T => String)(implicit logger : LoggerLike) : T = { logger.debug(f(value)) value } }
당신이 볼 수 있듯이 새로운 객체 생성의 오버 헤드가해서는 안, LogAny는 AnyVal이다.
이처럼 사용할 수 있습니다 :
scala> import utils.LogUtils._ scala> val a = 5 scala> val b = 7 scala> implicit val logger = play.api.Logger scala> val c = a + b @@ { c => s"result of $a + $b = $c" } c: Int = 12
당신은 결과에 대한 참조가 필요하지 않은 경우 또는, 단지 사용
scala> val c = a + b @@ "Finished this very complex calculation" c: Int = 12
이 구현에 대한 모든 단점?
편집하다:
여기 요지에 약간의 개선이 가능했습니다
-
==============================
6.스칼라 2.13부터 체이닝 연산 탭은 원래의 값으로 되돌 리면서 값에 (이 경우에 일부 기록) 부작용을 적용 할 수있다 :
스칼라 2.13부터 체이닝 연산 탭은 원래의 값으로 되돌 리면서 값에 (이 경우에 일부 기록) 부작용을 적용 할 수있다 :
예를 들어 :
scala> val a = 42.tap(println) 42 a: Int = 42
또는 우리의 경우 :
import scala.util.chaining._ def myFunc() = calcSomeResult().tap(x => logger.info(s"result is: $x"))
from https://stackoverflow.com/questions/9671620/how-to-keep-return-value-when-logging-in-scala by cc-by-sa and MIT license
'SCALA' 카테고리의 다른 글
[SCALA] 스칼라 스파크에서 NullPointerException이, 컬렉션 유형으로 발생하는 것으로 나타납니다? (0) | 2019.11.01 |
---|---|
[SCALA] 자바 8 요소의 고정 된 수의 여러 목록으로 분할 목록 (0) | 2019.11.01 |
[SCALA] 스칼라는 어떻게 목록에 발생 횟수를 셀 수 (0) | 2019.11.01 |
[SCALA] 스파크 스칼라 3 열에 1 열 분열 (0) | 2019.11.01 |
[SCALA] 무엇 setMaster을한다 '지역 [*]`스파크에 의미? (0) | 2019.11.01 |