컬렉션 및 목록

완료됨

특정 형식의 데이터가 둘 이상 있는 경우가 있을 텐데요. 이때 각 데이터에 대해 변수를 만들 수도 있고, 데이터를 모두 하나의 컬렉션으로 그룹화할 수도 있습니다. 데이터를 그룹화하면 개별 데이터가 아닌 컬렉션 전체에 메서드를 적용하는 등과 같은 이점을 얻을 수 있습니다.

컬렉션

컬렉션을 사용해야 한다는 것을 알 수 있는 첫 번째 신호는 같은 데이터 형식을 갖는 것으로 보이는 데이터가 여러 개 있는 경우입니다. 다음 코드와 같이 항목이 여러 개 있는 시나리오가 있을 수 있습니다.

let logEntryMonday = "first entry"
let logEntryTuesDay = "second entry"
let logEntryWednesday = "third entry"

위 코드에서, 비슷한 항목들을 하나씩 계속해서 추가할 수도 있겠지만, 어느 시점이 되면 이 방법이 실용적이지 않게 느껴지게 됩니다. 개별 항목들은 서로 관련이 있을 뿐 아니라 동일한 형식을 갖는데, 그렇다면 각 항목에 대해 하나씩 새 변수를 만드는 것이 좋은 생각일까요? 그렇지 않을 가능성이 있습니다. 그 대신 데이터 추가 또는 제거하기나 그 밖의 연산을 사용하는 등 논리적이고 관리가 편리한 그룹화를 사용할 수 있는 컬렉션을 사용하는 것이 좋습니다.

logEntriesWeek = ["first entry"; "second entry"; "third entry"]

위 코드는 입력해야 할 텍스트가 많지 않을 뿐만 아니라 관련 있는 모든 데이터가 하나로 질서 있게 그룹화됩니다.

그렇다면 F#에는 어떤 기능이 있을까요? F#은 각각 특정 상황에서 사용하도록 설계된 세 가지 형식의 컬렉션을 제공합니다.

  • 목록: F#의 목록은 동일한 형식을 갖는 요소들의 ‘변경 불가’하며 순서가 지정된 집합입니다. 변경 불가란 목록은 배열보다 더 나은 성능을 제공할 수 있지만 콘텐츠를 변경하려는 경우 배열을 사용할 때보다 더 복잡할 수 있음을 의미합니다. 예를 들어, 기존 목록에서 새 목록을 만들 수 있습니다. 이 단원에서는 목록을 사용하는 방법을 설명합니다.

  • 배열: 배열은 0부터 시작하는 인덱스로 연속적으로 정렬되는 동일한 형식의 데이터 요소로 구성된 고정 크기의 변경 가능한 컬렉션입니다. 변경 가능이란 데이터 요소를 쉽게 추가하고 제거할 수 있다는 의미인데, 성능 저하가 있을 수 있습니다.

  • 시퀀스: 시퀀스는 동일한 형식을 갖는 요소의 논리 계열입니다. 시퀀스는 요소를 모두 다 사용하지 않을 것으로 예상되는 경우에 대량의 데이터 컬렉션에 적합하기 때문에 Just-In-Time 특징을 갖습니다. 시퀀스는 또한 필요한 경우에만 요소를 계산합니다. 따라서 시퀀스는 특정 상황에서 목록보다 더 나은 성능을 제공할 수 있습니다.

목록

앞에서 설명했듯이 요소들의 목록은 변경 불가하며 순서가 지정된 집합입니다. 아래에서 목록을 만드는 몇 가지 방법을 살펴보세요.

let cards = ["Ace"; "King"; "Queen"]

이 코드에서는 요소를 대괄호([])로 묶어서 목록을 정의합니다. 목록의 각 항목은 세미콜론(;)으로 구분됩니다.

목록을 만드는 또 다른 방법은 아래 코드에서처럼 각 요소를 새 줄에 입력하는 것입니다. 이렇게 하면 세미콜론을 사용할 필요가 없습니다.

let cards = [
  "Ace"
  "King"
  "Queen"
]

목록에 포함된 요소는 형식이 동일해야 하므로 다음과 같은 선언은 허용되지 않습니다.

let cards = [ 1; "Ace"; "King" ] // not permitted

위 코드는 선언에 숫자와 문자열이 섞여 있으므로 허용되지 않습니다. 목록을 만드는 또 다른 방법은 범위 연산자(..)를 사용하는 것입니다. 시작 요소와 종료 요소를 범위 연산자(..)로 구분하여 지정하면 됩니다. 이렇게 하면 시작 요소부터 종료 요소까지의 모든 숫자가 만들어집니다. 예를 들어, 아래 코드를 사용하여 숫자 1 2 3 4 5를 만들 수 있습니다.

let numbers = [ 1 .. 5 ]

목록 변경하기

앞에서 설명했듯이 목록은 변경 불가합니다. 즉, 목록은 변경할 수 없습니다. 하지만 다르게 생각해 보면 목록에 하나의 항목을 추가하거나 항목으로 이루어진 목록을 추가하는 것처럼 만들 수 있습니다. 어떤 의미인가요? 다음 예제를 참조하세요.

let cards = ["Ace"; "King"; "Queen"]

위 코드는 세 개의 문자열로 구성된 목록입니다. 이중 콜론(::) 연산자를 사용하면 목록 맨 처음에 항목을 추가할 수 있습니다. 그러면 새 목록이 만들어지고, 기존 목록은 영향을 받지 않습니다.

let cards = ["Ace"; "King"; "Queen"]
let newList = "Jack" :: cards // "Jack", "Ace", "King", "Queen" 

아래와 같이 @ 연산자를 사용하여 목록을 추가할 수도 있습니다.

let cards = ["Ace"; "King"; "Queen"]
let otherCardList = ["Jack"; "10"]
let fullList = cards @ otherCardList // "Ace", "King", "Queen", "Jack", "10"

목록 모듈에는 append() 함수가 있는데, 이 함수는 배열 컬렉션과 시퀀스 컬렉션에서 사용할 수 있습니다. append()는 컬렉션 형식을 목록에서 시퀀스로 변경해야 하거나 항목을 추가하는 새로운 방법을 익히고 싶지 않을 때 사용하기 좋습니다. @ 기호를 사용하여 목록에 요소를 추가하는 방법은 목록 구조에서만 사용할 수 있습니다. 두 예제를 다시 살펴보고 대신 append()를 사용합니다.

let cards = ["Ace"; "King"; "Queen"]
let otherCardList = ["10"; "9"]
let fullList = cards |> List.append ["Jack"] // "Jack", "Ace", "King", "Queen"
let fullList = cards |> List.append otherCardList // "10", "9", "Ace", "King", "Queen"

속성

F#의 목록은 연결된 목록으로 구현됩니다. 즉, 목록은 요소가 다른 요소에 연결된 구조입니다. 그 밖에도 알아 두어야 하는 용어로 목록의 첫 번째 요소인 헤드와 헤드를 제외한 요소를 포함하는 테일이 있습니다. 예를 들어, 목록 1 2 3 4에서 헤드는 1이고 테일은 2 3 4입니다.

이 방식으로 요소를 구분하면 첫 번째 요소에 빠르게 액세스하여 읽어 들이거나, 제거하거나, 그 밖의 동작을 수행할 수 있습니다. 목록에 있는 특정 항목에 액세스하려면 0부터 시작하는 인덱스를 받는 Item 속성을 사용할 수 있습니다. 아래의 코드를 살펴보세요.

let list = [1; 2; 3; 4]
list.Item 1 // 2

다음 표에는 목록 속성이 나와 있습니다.

속성 설명
Head 목록의 첫 번째 요소
Empty 빈 목록을 반환합니다. 빈 목록을 만들 때 사용할 수 있습니다.
IsEmpty 현재 목록이 비어 있는지 확인합니다.
항목 지정된 위치(0부터 시작하는 인덱스)에 있는 현재 요소 검색
길이 목록의 항목 수 반환
Tail 목록의 첫 번째 요소를 제외한 모든 요소를 반환합니다.