람다를 인자로 받는 함수를 정의하려면? 타입을 어떻게 선언할지 알아 보자.
// 컴파일러에서 두 개를 함수 타입으로 추론을 함.
// 각각 Int unit 반환하는걸로 인식.
val sum = (x: Int, y: Int) => x + y
val action = { println(42) }
함수타입을 정의하려면 -> 뒤에 반환 타입을 지정하면 됨.
unit 타입 경우 생략해도 되지만 함수 타입! 을 선언할땐 반환타입 반드시 명시해야됨! Unit빼먹지말기.
// 널이 될수 있는 함수타입 변수도 가능.
var canReturnNull: ((Int, Int) -> Int)? = { x, y -> null }
고차함수를 구현해보자
 // Declare a function type parameter for operation that takes two Int arguments and returns an Int
 fun twoAndThree(operation: (Int, Int) -> Int) {
     // Call the operation function with arguments 2 and 3 and store the result in the result variable
     val result = operation(2, 3)
     // Print the result string
     println("The result is $result")
 }
 // Call the twoAndThree function with a lambda expression that adds two numbers
 twoAndThree { a, b -> a + b } // The result is 5
 // Call the twoAndThree function with a lambda expression that multiplies two numbers
 twoAndThree { a, b -> a * b } // The result is 6단순한 문법소개 함수 이름 뒤에 괄호를 넣고 원하는 인자넣은뒤 콤마로 분리!
Filter함수를 구현해 보자.

 // filter 함수를단순하게만든버전구현하기
fun String.filter(predicate: (Char) -> Boolean): String {
    val sb = StringBuilder()
    for (index in 0 until length) {
        val element = get(index)
        if (predicate(element)) sb.append(element)
    }
    return sb.toString()
}
println("ablc".filter { it in 'a'..'z' }) // Output: "abc"
길다 제목 참..
파라미터로 넘기는 함수를 디폴트값을 지정할 수 있다.
fun <T> Collection<T>.joinToString(
   separator: String = ", ",
   prefix: String = "",
   postfix: String = ""
): String {
   val result = StringBuilder(prefix)
   for ((index, element) in this.withIndex()) {
       if (index > 0) result.append(separator)
       result.append(element)
   }
   result.append(postfix)
   return result.toString()
}
예시 코드 result.append(element) 는 암시적이게 대해 toString 메서드 호출
허나 toString 메서드로만 고정되 문자열로 반환. 따라 서 다른 방법도필요
허나 파라미터를인자로 넘겨주면 불편할 수도 있으니 고정값을 사용하는법을 하자
  fun <T> Collection<T>.joinToString(
      separator: String = ", ",
      prefix: String = "",
      postfix: String = "",
      transform: (T) -> String = { it.toString() }
  ): String {
      val result = StringBuilder(prefix)
      for ((index, element) in this.withIndex()) {
          if (index > 0) result.append(separator)
          result.append(transform(element))
      }
      result.append(postfix)
      return result.toString()
  }
  val letters = listOf("Alpha", "Beta")
  
  // Example usage of joinToString function
println(letters.joinToString()) // default conversion
println(letters.joinToString(transform = { it.toLowerCase() })) // lowercase transformation
println(letters.joinToString(separator = "! ", postfix = "! ", transform = { it.toUpperCase() })) // uppercase transformation with custom separator and postfix
// Invoke 예제.
// Define a function-like object using a lambda expression
val myFunction: (String) -> Unit = { str -> println(str) }
// Call the function using the invoke method
myFunction.invoke("Hello, world!") // Output: "Hello, world!"
// Alternatively, call the function using the shorthand syntax
myFunction("Hello again!") // Output: "Hello again!"
fun <T> Collection<T>.joinToString(
    separator: String = ", ",
    prefix: String = "",
    postfix: String = "",
    transform: ((T) -> String)? = null
): String {
    val result = StringBuilder(prefix)
    for ((index, element) in this.withIndex()) {
        if (index > 0) result.append(separator)
        val str = transform?.invoke(element) ?: element.toString()
        result.append(str)
    }
    result.append(postfix)
    return result.toString()
}
enum class Delivery {
    STANDARD, EXPEDITED
}
class Order(val itemCount: Int)
fun getShippingCostCalculator(delivery: Delivery): (Order) -> Double {
    if (delivery == Delivery.EXPEDITED) {
        return { order -> 6 + 2.1 * order.itemCount }
    }
    return { order -> 1.2 * order.itemCount }
}
val calculator = getShippingCostCalculator(Delivery.EXPEDITED)
println("Shipping costs: ${calculator(Order(3))}")
Shipping c o s t s 12.3data class SiteVisit(
  val path: String,
  val duration: Double,
  val os: OS
)
enum class OS {
  WINDOWS, LINUX, MAC, IOS, ANDROID
}
val log = listOf(
  SiteVisit("/", 34.0, OS.WINDOWS),
  SiteVisit("/", 22.0, OS.MAC),
  SiteVisit("/login", 12.0, OS.WINDOWS),
  SiteVisit("/signup", 8.0, OS.IOS),
  SiteVisit("/", 16.3, OS.ANDROID)
)
// 확장함수로 간결하게
fun List<SiteVisit>.averageDurationFor(os: OS) =
  filter { it.os == os }
      .map(SiteVisit::duration)
      .average()
val averageWindowsDuration = log.filter { it.os == OS.WINDOWS }
  .map(SiteVisit::duration)
  .average()
println(averageWindowsDuration) // Output: 23.0
println(log.averageDurationFor(OS.MAC)) // Output: 22.0
// 고차함수를 이용해 간결하게
fun List<SiteVisit>.averageDurationFor(predicate: (SiteVisit) -> Boolean) =
  filter(predicate)
      .map(SiteVisit::duration)
      .average()
println(log.averageDurationFor {
  it.os in setOf(OS.ANDROID, OS.IOS)
}) // Output: 12.15
println(log.averageDurationFor {
  it.os == OS.IOS && it.path == "/signup"
}) // Output: 8.0
inline 함수에 사용시 함수의 본문이 인라인이 됨.
함수 본문을 번역해서 바이트코드로 컴파일 함.
그러나 람다를 변수에 저장하거나 나중에 사용하는 경우 인라이닝 불가능
val anotherLambda: () -> Unit
if (someCondition) {
    anotherLambda = myLambda
} else {
    anotherLambda = { println("Performing another action") }
}
inlineFunction(anotherLambda) // In this case, inlining is not possible
		```
그러나 인라이닝은 컴파일된 코드의 크기를 증가해 항상 유익한건 아님.
과도한 사용은 코드 부풀림 발생 -> 캐시미스 유발 컴파일 코드 효율성 떨어 트림..
   inline fun add(a: Int, b: Int): Int {
       return a + b
   }
   inline fun processList(list: List<Int>, action: (Int) -> Int): List<Int> {
       return list.map(action)
   }
   inline fun square(x: Int): Int {
       return x * x
   }
   fun main() {
       // Small function example
       val sum = add(3, 4)
       println("Sum: $sum")
       // Higher-order function example
       val doubled = processList(listOf(1, 2, 3)) { it * 2 }
       println("Doubled: $doubled")
       // Loop optimization example
       println("Squared:")
       for (i in 1..5) {
           println(square(i))
       }
   }
Sum: 7
Doubled: [2, 4, 6]
Squared:
1
4
9
16
25
	```컬렉션에 작용하는 코틀린 표준 라이브러리 성능 알아보기
        //람다 구현 
  data class Person(val name: String, val age: Int)
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.filter { it.age < 30 }) // [Person(name=Alice, age=29)]
      //람다 구현 X
data class Person(val name: String, val age: Int)
val people = listOf(Person("Alice", 29), Person("Bob", 31))
val result = mutableListOf<Person>()
for (person in people) {
    if (person.age < 30) result.add(person)
}
println(result) // [Person(name=Alice, age=29)]
                        코틀린의 Filter함수는 인라인 함수 임.
filter 함수가 호출될 때, 람다 표현식의 바이트 코드와 함께 호출 위치에 직접 복사된다는 것을 의미합니다. 이로 인해 성능 면에서 우수한 최적화된 코드가 생성 됨
람다 함수를 컬렉션과 함께 인라인으로 사용되는 경우가 많으며
이런식으로 성능이 향상이 됨.
그래서 일반함수를 사용할때 필터 대신 사용하면 해당 함수를 별도로 호출해서
오버헤드가 추가되고 성능상 안좋을 수 있음
  data class Person(val name: String, val age: Int)
  fun main() {
      val people = listOf(Person("Alice", 29), Person("Bob", 31))
      // using lambda function
      val lambdaResult = people.filter { it.age < 30 }
      println(lambdaResult)
      // using regular function
      val regularResult = getYoungerThan30(people)
      println(regularResult)
  }
  fun getYoungerThan30(people: List<Person>): List<Person> {
      val result = mutableListOf<Person>()
      for (person in people) {
          if (person.age < 30) {
              result.add(person)
          }
      }
      return result
  }여기 예제에서도 결과는 같지만 일반 함수는 함소 호출이 추가 되므로 오버헤드발생.
책에서는 시퀀스(stream) 사용시 인라인 하지 않기 때문에 작은 크기 컬렉션은 일반 컬렉션 연산이 더나음
대략적으로 10-100개 미만의 요소가 있는 컬렉션의 경우 성능 차이가 크지 않을 수 있음 실무에선 .. 그냥 람다써도 될 듯?
                            // 함수 호출 비용 절감
  fun add(a: Int, b: Int): Int {
      return a + b
  }
  // Lambda function
  val addLambda: (Int, Int) -> Int = { a, b -> a + b }
  // Usage
  val result1 = add(2, 3) // Function call
  val result2 = addLambda(2, 3) // Lambda call
  
  
  
  // 개체 생성 방지:
  // 이 예에서 performAction 함수는 람다를 인수로 사용합니다. 
  // 람다 호출을 사용하여 호출하면 람다에 해당하는 객체가 생성되지 않습니다. 
  // 반대로 개체 식을 사용하여 호출하면 람다 인터페이스를 구현하는 익명 클래스가 생성됩니다.
  fun performAction(action: () -> Unit) {
      action()
  }
  // Usage
  performAction { println("Hello World") } // Lambda call
  // Equivalent code using object expression
  performAction(object : () -> Unit {
      override fun invoke() {
          println("Hello World")
      }
  })
  
  
  // 비로컬 제어 흐름
  // 비로컬 제어 흐름은 중간 코드를 건너뛰고
  // 프로그램의 한 지점에서 다른 지점으로 제어를 전송하는 기능
inline fun performCalculation(numbers: List<Int>, operation: (Int) -> Int): Int {
    numbers.forEach {
        val result = operation(it)
        if (result < 0) return result // Non-local return
    }
    return 0
}
  // Usage
  val numbers = listOf(1, 2, 3, -4, 5)
  val result = performCalculation(numbers) {
      if (it < 0) return@performCalculation -1 // Labelled return
      it * 2
  }
  println(result) // -1
자원을 획득하고 작업을 마친후 자원을 해제하는 자원관리?
자바에서는 try-with-resource를써 해당객체를 획득하고 자동으로 닫아준다.
코틀린에선 use라는 예약어가있음
fun readFirstLineFromFile(path: String): String {
    BufferedReader(FileReader(path)).use { br ->
        return br.readLine()
    }
}새로운사실! use는 함수를 닫을 수 있는(Closeable)자원에 대한 확장 함수다! (람다인자로받음)
use는 또 인라인 함수임.
람다 본문안에 사용한 Return은 넌로컬 return 이문은 람다가 아니라 readFristLineFromFile 함수를 끝내면서 값 반환.
외부 함수나 클로저에서 반환하는 반환문을 의미
비로컬 반환문은 여러 수준의 중첩된 함수나 클로저에서 빠져나올 수 있도록 해줌
책에서는 자신을 둘러싸고 있는 블록보다 더 바깥에 있는 다른블록을 반환하게 만드는 것을 말한다.
책에 나온내용이 더이해하기 쉽네..
바깥쪽에 함수를 반환시킬수 있는.. 음음.. 람다를 인자로 받는함수가 인라인함수인경우뿐
람다식의 로컬 Return 사용 가능 , 이는 break랑 비슷함
fun lookForAlice(people: List<Person>) {
  people.forEach label@{
      if (it.name == "Alice") return@label
  }
  println("Alice might be somewhere")
}
label을 붙여서 표시함.