검색결과 리스트
map에 해당되는 글 2건
- 2015.06.22 [swift] Map, Filter, Reduce
- 2011.04.11 컬렉션(Collection)
글
[swift] Map, Filter, Reduce
함수를 인자로 취하는 함수를 고차원 함수(higher-order function)라고 합니다.
Swift 표준라이브러리에서 array의 메서드로 제공하는 고차원 함수인 map, filter, reduce에 대해서 알아보겠습니다.
1. Map
func map<U>(transform: (T) -> U) -> Array<U>
Map은 배열 각 요소 x에 변환함수 transform을 적용하고 그 결과값으로 구성된 배열을 반환하는 함수입니다.
배열요소들을 다른 값으로 맵핑하는 함수이지요.
2. Filter
func filter(includeElement: (T) -> Bool) -> Array<T>
Filter는 조건식을 인자로 받아, 조건식이 true를 만족하는 요소들로만 구성된 배열을 반환하는 함수입니다.
쉽게 말하면, 배열요소들을 필터링하는 함수입니다.
3. Reduce
func reduce<U>(initial: U, combine: @noescape (U, T) -> U) -> U
Reduce는 U를 초기값으로 하여,각 배열요소들과 순차적으로 결합연산을 하여 누적된 단일값을 반환하는 함수입니다.
4. 사용예
이제 사용법에 대해서 알아볼까요? 다음과 같은 정수형 배열이 있습니다.
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- 1. 정수형 배열의 각 요소에 1을 더한 배열을 얻고 싶다.
- 2. 정수형 배열의 각 요소에 2를 곱한 배열을 얻고 싶다.
- 3. 정수형 배열에서 짝수로만 구성된 배열을 얻고 싶다.
- 4. 정수형 배열 값들의 총합을 얻고 싶다.
map 함수를 사용하여 1,2번 문제의 답을 쉽게 얻을 수 있습니다.
// 1. 정수형 배열의 각 요소에 2를 곱한 배열을 얻고 싶다.
arr.map { (x) -> Int in
return x+1
}
// 2. 정수형 배열의 각 요소에 2를 곱한 배열을 얻고 싶다.
arr.map { (x) -> Int in
return x*2
}
filter를 사용하여 3번을 해결해보겠습니다.
// 3. 정수형 배열에서 짝수로만 구성된 배열을 얻고 싶다.
arr.filter { (x) -> Bool in
return x%2 == 0
}
당연히 4번은 reduce의 몫입니다.
// 4. 정수형 배열 값들의 총합을 얻고 싶다.
arr.reduce(0, combine: { (result, x) -> Int in
return result + x
})
Trailing Closure 를 사용하여 다소 낯선 형태지만 코드가 너무 짧아 놀라셨죠?
(Trailing Closure는 함수의 마지막 인자가 클로져(함수)일 경우, 클로져를 함수 호출부의 꼬리(trailing)에 표기하는 방식입니다. )
이러한 고차원 함수들을 복합적으로 사용하여 프로그래밍하는 방식을 함수형 프로그래밍이라고 합니다.
함수형 프로그래밍은 짧고 명료한 코드를 작성할 수 있게하고, 버그의 가능성 또한 훨씬 줄인다고 합니다.
낯선 함수형 프로그래밍 방식에 익숙해지려면 다소 시간이 걸리겠지요? ^^
5. Map 함수 구현하기
학습한 내용을 좀더 잘 이해하기 위해 직접 map 함수를 만들어 보겠습니다.
일반적인 방법으로 1,2번 문제를 구현하는 코드를 작성했습니다.
var arr = [1, 2, 3, 4, 5]
func pluseOne(arr:[Int]) -> [Int] {
var result:[Int] = []
for x in arr {
result.append(x + 1)
}
return result
}
func multiplyTwo(arr:[Int]) -> [Int] {
var result:[Int] = []
for x in arr {
result.append(x * 2)
}
return result
}
두 함수는 연산하는 행위만 제외하고 거의 비슷합니다. 연산하는 행위를 함수로 묶어내면 일반화가 가능해보이네요.
func map(arr:[Int], transform:(Int->Int)) -> [Int] {
var result:[Int] = []
for x in arr {
result.append(transform(x))
}
return result
}
이제 제네릭을 사용하여 범용타입으로 대치하면
func map<T, U>(arr:[T], transform:(T->U)) -> [U] {
var result:[U] = []
for x in arr {
result.append(transform(x))
}
return result
}
map 함수가 완성되었습니다. 우리는 글로벌 함수로 구현했지만 표준라이브러리에서는 array의 메소드로 제공한다는 점이 유일한 차이입니다.
5. Filter 함수 구현하기
Map 함수를 구현해보아서 Filter 함수 구현은 좀더 간단합니다.
// 일반적인 방법으로 구현하고
func filter(arr:[Int]) -> [Int] {
var result:[Int] = []
for x in arr {
if x%2 == 0 {
result.append(x)
}
}
return result
}
// 함수로 공통 행위를 묶어낸 다음
func filter(arr:[Int], includeElement:(Int->Bool)) -> [Int] {
var result:[Int] = []
for x in arr {
if includeElement(x) {
result.append(x)
}
}
return result
}
// 제네릭 적용
func filter<T>(arr:[T], includeElement:(T->Bool)) -> [T] {
var result:[T] = []
for x in arr {
if includeElement(x) {
result.append(x)
}
}
return result
}
6. Reduce 함수 구현하기
단계적으로 구현해보니 고차원 함수 구현이 어렵지 않죠? 이제 reduce를 구현해 볼까요?
// 일반적으로 구현하고
func reduce(arr:[Int]) -> Int {
var result:Int = 0
for x in arr {
result = result + x
}
return result
}
// 초기값 할당과 누적하는 행위를 파라미터로 분리하고
func reduce(arr:[Int], initial:Int, combine:(Int,Int)->Int) -> Int {
var result:Int = initial
for x in arr {
result = combine(result, x)
}
return result
}
// 제네릭 적용
func reduce<T, U>(arr:[T], initial:U, combine:(U, T)->U) -> U {
var result:U = initial
for x in arr {
result = combine(result, x)
}
return result
}
지금까지 Swift 표준라이브러리의 Map, Filter, Reduce에 대해 알아보았습니다.
'Swift' 카테고리의 다른 글
| [swift2] Xcode7 beta2 Swift 언어 변경사항 (0) | 2015.06.24 |
|---|---|
| [swift] 문자열 Indexing과 Slicing (0) | 2015.06.23 |
| [swift] Map, Filter, Reduce (0) | 2015.06.22 |
| [swift2] New in Swift2.0 (0) | 2015.06.19 |
| [swift] Apple Swift Blog (0) | 2015.06.10 |
| [swift] Swift 기초 - 4 (0) | 2014.10.29 |
설정
트랙백
댓글
글
컬렉션(Collection)
의미에서 컨테이너라고 하며, 최상위 추상클래스 Collection의 이름을 따서 컬렉션이라고 부르기도 한다.
데이터를 다루는 방법과 데이터에 대한 조작 연산(CRUD)의 빈도에 따라 최적화되어 있는 다양한 자료구조들을 제공하고 있다.
이 컬렉션의 클래스들에 대한 모두 기억할 필요는 없다. 데이터 추가/조회/수정/삭제 중 어떤 연산이 많은 빈도를 가지고 있는지에 따라 어떤 컬렉션 클래스가 적합한지 알고 사용하면 되겠다. 각 컬렉션들에 대한 구체적인 설명은 생략하겠다.
컬렉션류 클래스들의 전체 상속도는 다음과 같다
컬렉션 클래스들 중 Set 과 HashMap 대한 특성을 알아보자.
기본형 타입 외 사용자 정의타입에 대해 Set을 사용하면 데이터들의 중복이 없으며 element 들의 순서를 유지하지 않는다.
(집합과 같이 데이터들의 순서가 중요하지 않으며, 데이터들이 중복없이 하나만 고유하게 존재할 경우 사용한다)
| Set (인터페이스) | 중복요소 저장 안됨. 추가되는 요소는 객체의 동일성 판별을 위해 equals() 메소드를 정의해야함. Collection 과 인터페이스가 동일하며, 요소(element)들의 순서유지가 보장되지 않음. |
| HashSet | 빠른 검색이 가능한 Set. 요소는 equals(), hashCode()를 정의해야함. |
| TreeSet | 트리형태의 순서를 갖는 Set. 정렬된 요소를 얻을 수 있으며, Comparable 인터페이스를 구현해야 함. |
| LinkedHashSet | HashSet의 빠른 검색속도를 갖으며, 내부적으로 LinkedList의 순서를 유지함. 추가된 순서대로 요소를 얻을 수 있음. hashCode() 메소드 또한 정의해야함. |
Set을 사용자 정의 타입에서(클래스에) 사용하려면, 중복성 회피를 위해 equals() 메소드를 재정의 해주어야한다.
(Set는 내부적으로 element 들의 equals() 메소드를 호출하여 서로간의 중복 여부를 판단하기 때문이다)
hashCode()는 hashSet, LinkedHashSet 를 사용할 때만 재정의 해주면 된다. 재정의를 하지 않으면, Object의 hashCode()를
사용하는데, equals()를 구현했어도 객체가 중복되어 Set 인터페이스 규약이 깨지게 된다. (서로 동일한 데이터를 가지고 있어도hashCode()를 구현하지 않으면 다른 버킷으로 해싱되기 때문 데이터가 중복될수 있다. 이것은 Set의 규약을 위반한 것임)
hashCode(), equals() 을 모두 재정의 하는 것이 바람직한 습관이다.
key-value 관계를 저장하여 key를 사용한 value의 검색을 가능하게 함.
| HashMap | 해시 테이블에 기초한 구현 클래스. 객체 추가 / 검색어에 동일한 시간을 보장함. |
| LinkedHashMap | HashMap과 동일한 저장방식을 사용함. 이터레이터를 사용함. key-value 쌍이 추가된 순서, 최근 최소 사용한 빈도에 따른 검색이 가능 HashMap 에 비해 다소 느리고, 순차검색시 내부적으로 연결리스트를 사용해 더빠른 성능을 발휨. |
| TreeMap | 이진 트리에 기초한 맵 구현클래스. key-value 쌍은 Comparable, Comparator에 의해 정렬순서가 저장됨. 하위 트리를 반환하는 subMap() 메소드를 보유함. |
| WeakHashMap | 더 이상 참조되지 않은 key에 대한 가비지 컬렉션을 허용함. |
| ConcurrentHashMap | 동기화에서 락을 발생시키지 않는 Thread-Safe한 Map. |
| IdentityHashMap | key 비교시 equals()가 아닌 == 사용함. 범용사용은 불가능함. |
예제
import java.util.*;
// 모든 Set 컬렉션은
// 중복 회피를 위해 equals()를 재정의 해야함
class SetType {
int i;
public SetType(int n) {
i = n;
}
public boolean equals(Object o) {
return o instanceof SetType && (i == ((SetType)o).i);
}
public String toString() {
return Integer.toString(i);
}
}
// HashSet 계열은 equals() 외에 hasCode()를 재정의 해야함
// 그렇지 않을 경우 equals()를 정의했어도, 데이터 중복이 발생
class HashType extends SetType {
public HashType(int n) {
super(n);
}
public int hashCode() {
return i;
}
}
// TreeSet 계열은 equals() 외에 compareTo()를 재정의 해야함
// compareTo()를 재정의 하지않으면 실행시 ClassCastException 발생
class TreeType extends SetType implements Comparable<treetype>
{
public TreeType(int n) {
super(n);
}
public int compareTo(TreeType arg) {
return (i > arg.i ? 1 : (i == arg.i) ? 0 : -1);
}
}
public class TypesForSets {
static <t> Set<t> fill(Set<t> set, Class<t> type) {
try {
for (int i = 0; i < 10; i++) {
// int형 인자를 갖는 생성자를 얻어서, 객체 생성
set.add(type.getConstructor(int.class).newInstance(i));
}
} catch(Exception e) {
throw new RuntimeException(e);
}
return set;
}
static <t> void test(Set<t> set, Class<t> type) {
// 객체 중복저장 시도 - 셋의 특성을 알아보기 위함
fill(set, type);
fill(set, type);
fill(set, type);
System.out.println(set);
}
public static void main(String[] args) {
// HashSet 계열은 equals() 와 hashCode()를 재정의 해야함
test(new HashSet(), HashType.class);
test(new LinkedHashSet(), HashType.class);
// TreeSet 계열은 equals() 와 compareTo()를 재정의 해야함
test(new TreeSet(), TreeType.class);
// 아래메소드들은 각 Set 컬렉션의 특성이 파괴됨
test(new HashSet(), SetType.class);
test(new HashSet(), TreeType.class);
test(new LinkedHashSet(), SetType.class);
test(new LinkedHashSet(), TreeType.class);
try {
test(new TreeSet(), SetType.class);
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
test(new TreeSet(), HashType.class);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
'언어로그 > Java' 카테고리의 다른 글
| [java] 제13회 한국자바개발자 컨퍼런스 (0) | 2013.01.30 |
|---|---|
| Inner Class(내부 클래스) (0) | 2011.04.12 |
| 컬렉션(Collection) (0) | 2011.04.11 |
| 리플렉션으로 Getter 와 Setter 검사하기 (0) | 2011.04.07 |
| (2) 리플렉션(Reflection) 사용하기 (0) | 2011.04.07 |
| (1) 리플렉션(Reflection) (3) | 2011.04.07 |