ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Scala]8-함수와 클로저
    scala 2020. 9. 24. 23:11

    P190

    지역 함수는 감싸고 있는 블록의 파라미터에 접근할 수 있기 때문에, 이럴 필요가 없다. 바깥쪽 processLine 함수의 파라미터를 그냥 사용할 수 있다.

    import scala.io.Source
    
    object LongLines {
    	
    	def processFile(filename: String, width: Int) {
    		
    		def processLine(line: String ) {
    		
    			if (line.length > width) 
    			println(filename +": "+ line.trim)
    		}
    		
    		val source = Source.fromFile(filename)
    		for (line <- source.getLines())
    			processLine(line)
    	}
    	
    
    }

    P191

    1급 계층 함수란 말은 '함수가 일급 시민(first-class citizen)이다'라고도 표현한다. 사회 계층에 빗대 제약 없는 값을 일컫는 말로, 본문에 있는 대로 할당, 인자로 넘김, 함수에서 반환이 가능한 특징을 가져야 한다.

    scala> var increase = (x: Int) => x + 1
    increase: Int => Int = $$Lambda$1020/954936400@8bffb8b
    
    scala> increase(10)
    res0: Int = 11

    P194

    함수 리터럴을 좀 더 간결하게 만들기 위해 밑줄을 하나 이상의 파라미터에 대한 위치표시자로 사용할 수 있다.

    scala> someNumbers.filter(_ > 0)
    res4: List[Int] = List(5, 10)

    P197

    sum _은 실제로 부분 적용 함수이지만,왜 부분 함수인지 명확하게 와 닿지 않을 것이다. 부분 적용이라는 이름은 함수를 적용할 때 인자를 모두 넘기지 않았기 때문이다.

    scala> def sum(a: Int, b: Int, c: Int) = a + b + c
    sum: (a: Int, b: Int, c: Int)Int
    
    scala> sum(1,2,3)
    res6: Int = 6
    
    scala> val a = sum _
    a: (Int, Int, Int) => Int = $$Lambda$1086/978105475@4d770bcd
    
    scala> a(1,2,3)
    res7: Int = 6
    
    scala> a.apply(1,2,3)
    res8: Int = 6
    
    scala> val b = sum(1, _: Int, 3)
    b: Int => Int = $$Lambda$1087/1954051593@36cdcae0
    
    scala> b(2)
    res9: Int = 6

     

    P199

    주어진 함수 리터럴로부터 실행 시점에 만들어낸 객체인 함수 값(객체)을 클러저(closure)라고 부른다. 클로저라는 이름은 함수 리터럴의 본문에 있는 모든 자유 변수에 대한 바인딩 binding을 '포획capturing' 해서 자유 변수가 없게 '닫는closing'행위에서 따온 말이다.

    scala> var more = 1
    more: Int = 1
    
    scala> val addMore = (x: Int) => x + more
    addMore: Int => Int = $$Lambda$1047/1621939721@65ec8b24
    
    scala> addMore(10)
    res1: Int = 11
    
    
    ---
    
    
    scala> more = 9999
    more: Int = 9999
    
    scala> addMore(10)
    res2: Int = 10009
    
    ---
    
    scala> val someNumbers = List(-11, -10, -5, 0, 5, 10)
    someNumbers: List[Int] = List(-11, -10, -5, 0, 5, 10)
    
    scala> var sum = 0
    sum: Int = 0
    
    scala> someNumbers.foreach(sum += _)
    
    scala> sum
    res4: Int = -11
    
    ---
    scala> def makeIncreaser(more: Int) = (x: Int) => x + more
    makeIncreaser: (more: Int)Int => Int
    
    scala> val inc1 = makeIncreaser(1)
    inc1: Int => Int = $$Lambda$1105/537729597@7de147e9
    
    scala> val inc9999 = makeIncreaser(9999)
    inc9999: Int => Int = $$Lambda$1105/537729597@635ff2a5
    
    scala> inc1(10)
    res5: Int = 11
    
    scala> inc9999(10)
    res6: Int = 10009
    

    P202

    반복 파라미터

    scala> def echo(args: String*) = for (arg <- args) println(arg)
    echo: (args: String*)Unit
    
    scala> echo()
    
    scala> echo("one")
    one
    
    scala> echo("hello","world!")
    hello
    world!
    
    scala> val arr = Array("what's", "up", "doc?")
    arr: Array[String] = Array(what's, up, doc?)
    
    scala> echo(arr)
    <console>:14: error: type mismatch;
     found   : Array[String]
     required: String
           echo(arr)
                ^
    
    scala> echo(arr: _*)
    what's
    up
    doc?

    P203

    이름 붙인 인자

    scala> def speed(distance: Float, time: Float): Float = distance / time
    speed: (distance: Float, time: Float)Float
    
    scala> speed(100,10)
    res15: Float = 10.0
    
    scala> speed(distance = 100, time = 10)
    res16: Float = 10.0
    
    scala> speed(time = 10, distance = 100)
    res17: Float = 10.0
    

    P203

    디폴트 인자 값

    scala> def printTime(out: java.io.PrintStream = Console.out) = out.println("time = "+ System.currentTimeMillis())
    printTime: (out: java.io.PrintStream)Unit
    
    scala> printTime()
    time = 1602406935750
    
    scala> def printTime2(out: java.io.PrintStream = Console.out, divisor: Int = 1) = out.println("time = "+ System.currentTimeMillis()/divisor)
    printTime2: (out: java.io.PrintStream, divisor: Int)Unit
    
    scala> printTime2(out = Console.err)
    time = 1602407019340
    
    scala> printTime2(divisor = 1000)
    time = 1602407049

    P206

    두 함수 중에서 어느 쪽이 더 좋을까? 간결성이나 var를 피한다는 측면에서는 함수형이 우세하다. 하지만 명령형 스타일의 접근이 좀 더 효율적이지 않나?보통 루프의 끝에서 시작 부분으로 가는 것보다 재귀호출이 휠씬 비용이 많이 드는 것처럼 보이기 때문에 조금 의아할지도 모르겠다.

    하지만 위의 근사치 추정과 같은 경우에는 스칼라 컴파일러가 중요한 최적화를 적용할 수 있다. 함수 approximate의 본문을 계산하는 과정에서 맨 마지막에 벌어지는 일이 재퀴 호출임에 주목하자. approximate 함수 처럼 마지막에 자신을 재귀호출하는 경우를 꼬리 재귀 tail recursive 라고 한다.

Designed by Tistory.