스위프트에서 Closures는 다른 언어에서의 Lambda와 비슷하다.

let closuresTest:(Int) -> (String) = { _ in
    
    return "Hello, Polytech!"
}

let result = closuresTest(0)
print(result)

결과 : Hello, Polytech!

 

이유는 모르겠지만... 인풋 없이 하고 싶은데 계속 에러가 난다... 이런식으로라도 그냥 의미 없게라도 넣어야 작동한다...

let closuresTest:(Bool) -> (String) = { _ in
    
    return "Hello, Polytech!"
}

let result = closuresTest(false)	// () 안의 값은 아무 의미도 없다... 그냥 arguments를 지정하지 않을 경우 누락되었다고 에러가 발생해서 형식상 넣어준 것이다.
print(result)

결과 : Hello, Polytech!

sort에 closures를 적용시켜보자.

 

Swift의 내장 함수 sort는 기본적으로 오름차순 정렬만 된다. 내림차순을 하려면 어떻게 해야할까?

 

1. sort 후 reverse 하기

var testArray:Array<Int> = [4, 6, 8, 1, 3]
print(testArray)

결과 : [4, 6, 8, 1, 3]


var testArray = [4, 6, 8, 1, 3]
testArray.sort()

print(testArray)

결과 : [1, 3, 4, 6, 8]


var testArray = [4, 6, 8, 1, 3]
testArray.sort()
testArray.reverse()

print(testArray)

결과 : [8, 6, 4, 3, 1]

하지만 이렇게 정렬하는 것은 불필요한 코드가 생기고 불필요한 계산이 추가된다.

 

2. 정렬을 내가 정의한 함수의 알고리즘으로 작동시키기

insertion sort는 배열에서 2개의 숫자를 비교한 뒤 Boolean 값이 True인 것만 반환하여 정렬한다.

 즉, 기본이 오름차순 정렬인 스위프트에서 정렬을 시작하면, 배열의 index 0, index 1 2개의 값이 sort 함수로 들어가고, 그 sort 함수는 이 2개의 값을 비교한다.
 [5, 3, 7] 이라는 배열이 있다. 이 때 index 0의 값을 잠시 number1이라 하자. 마찬가지로, index 1의 값을 잠시 number2라 하자.
그러면 number1(=5) > number2(=3)이기 때문에 return 값으로 number1 < number2를 반환하고 싶지만 결과가 false가 된다.
 따라서 이번에는 다시 두 수의 순서를 바꾸어 비교를 해본다. 그러면 number(=3) < number2(=5) 이므로 true가 된다. 따라서, index 0과 index 1은 순서가 바뀌게 된다. 즉, 이제 [3, 5, 7]이 된거다.
 그러면 또 다시 index 0과 index 1이 뒤바뀐 [3, 5, 7]을 가지고 비교를 한다. 이번에는 새 index 1의 값을 잠시 number1이라 하고, index 2의 값을 잠시 number2라 하자. 그러면 number1(=5) < number2(=7)이기 때문에 return 값으로 number1 < number가 바로 true가 되고 이를 그대로 반환한다. 즉, 최종적으로 [3, 5, 7]로 정렬이 되는 것이다. 배열의 길이가 짧든 길든 이런 과정을 배열의 마지막까지 반복해서 정렬을 한다.

따라서, 이 때 sort가 반환하는 return되는 Bool 값의 True 값을 바꾸면 Ascending Sort를 Descending Sort로 바꿀 수 있는 것이다. 따라서 함수는 (Int, Int) -> Bool 형식이 되어야 한다.

func sortASC(num1: Int, num2: Int) -> Bool {
   return num1 < num2
}

즉, 이런 형태의 Bool 로직을 반대로 바꿔주면 된다.

func sortDESC(num1: Int, num2: Int) -> Bool {
   return num1 > num2
}

해설 : 앞에 오는 숫자가 뒤에 오는 숫자보다 더 크면 true고 이를 return하라는 것이다. 즉, 큰수 -> 작은수 순으로 정렬된다.

 

그리고 sort의 Bool판단 로직을 내가 정의한 함수로 작동시킨다.

var numbers = [4, 6, 8, 1, 3]

func sortDESC(num1: Int, num2: Int) -> Bool {
   return num1 > num2
}

numbers.sort(by: sortDESC)

결과 : [8, 6, 4, 3, 1]

 

마찬가지로 문자열 정렬일 경우는 (String, String) -> Bool 형식이 되어야 한다.

var name = ["Bella", "Elin", "Matilda", "Raina", "Kate", "Alice"]

func sortDESC(s1: String, s2: String) -> Bool {
    return s1 > s2
}
name.sort(by: sortDESC(s1:s2:))

결과 : ["Raina", "Matilda", "Kate", "Elin", "Bella", "Alice"]

 

3. Closures 익명함수를 써서 위 코드를 줄여보자.

다른 언어에서 Lambda와 비슷한 역할을 하는 것이 스위프트에서는 Closures라는 것이 있다.

func sortDESC(num1: Int, num2: Int) -> Bool {
   return num1 > num2
}

// 에서 함수의 이름을 지우고 함수 전체를 {}로 감싼다.
// 즉, 함수 이름을 지운 다음 그 자리(num1: Int 앞부분)에 { 를 입력하고
// 함수 전체를 {}로 감쌌기 때문에 Bool 뒤에 따로 함수의 로직만 분리해두었던 { 의 시작 부분은 in으로 바꿔준다.
// 최종 형태는 다음과 같다.

{(num1: Int, num2: Int) -> Bool in
   return num1 > num2
}

// 이제 이것을 함수의 이름을 넣었던 자리에 익명으로 함수 자체를 넣어버리자. 따라서

numbers.sort(by: sortDESC)

// 는 이제 이렇게 바꿔 쓸 수 있다.

numbers.sort(by: {(num1: Int, num2: Int) -> Bool in
   return num1 > num2
})

결과 : [8, 6, 4, 3, 1]

 

4. 위 익명함수를 줄여보자. (Closures 간략하게 줄이기)

스위프트에서는 인풋 파라미터의 자료형을 명확히 지정하지 않아도 스스로 유추가 가능하다. 따라서 위 로직은 더 짧게 바꿀 수 있다. 아예 num1, num2도 n1, n2로 줄여보자.

var numbers = [4, 6, 8, 1, 3]

numbers.sort(by: {n1, n2 -> Bool in return n1 > n2})
print(numbers)

결과 : [8, 6, 4, 3, 1]

 

5. 이 익명함수를 더 줄요보자. (Closuers 더 간략하게 줄이기)

스위프트의 closures는 파라미터를 $0, $1, $2, ... 로 축약할 수 있다. 따라서 위 코드는 다음과 같이 줄일 수 있다. 심지어 위에서는 자료형을 생략했지만 이제 인풋 파라미터를 명시하는 것 조차 생략할 수 있다.

var numbers = [4, 6, 8, 1, 3]

numbers.sort(by: {$0 > $1})
print(numbers)

결과 : [8, 6, 4, 3, 1]

 

 

 

더보기

cf. 정렬을 삼항 연산자와 Closures를 사용하기(오름차순)

data.sort (by: {sortedColumn.sorting == .ascending ? $0[sortedColumn.column] < $1[sortedColumn.column] : $0[sortedColumn.column] > $1[sortedColumn.column]})

위 수식을 아래와 같이 사용할 수도 있다.

data.sort {
  let ascending = $0[sortedColumn.column] < $1[sortedColumn.column]
  return sortedColumn.sorting == .ascending ? ascending : !ascending  // ? true -> 위에 let ascending : false -> ! 붙어서 < 가 반대로 뒤집혀 > 가 되니 descending으로 작동
}

 

 

 

 

 

사실... 파라미터 축약 마저도 생략하고 써도 내장함수에 정의되어 있어서 정렬된다...

var numbers = [4, 6, 8, 1, 3]

numbers.sort()          // 결과 : [1, 3, 4, 6, 8], inplace=true
numbers.sort(by: <)     // 결과 : [1, 3, 4, 6, 8], inplace=true
numbers.sort(by: >)     // 결과 : [8, 6, 4, 3, 1], inplace=true

numbers.sorted()        // 결과 : [1, 3, 4, 6, 8], inplace=false
numbers.sorted(by: <)   // 결과 : [1, 3, 4, 6, 8], inplace=false
numbers.sorted(by: >)   // 결과 : [8, 6, 4, 3, 1], inplace=false

 

+ Recent posts