Share via


배열(F#)

배열은 연속적인 데이터 요소의 변경 가능한 컬렉션입니다. 이는 크기가 정해져 있고 0부터 시작합니다. 배열에 포함된 요소의 형식은 모두 동일해야 합니다.

배열 만들기

여러 가지 방법으로 배열을 만들 수 있습니다. 작은 배열은 다음 예제에서와 같이 [| 및 |] 사이에 세미콜론으로 구분된 연속적인 값을 나열하여 만들 수 있습니다.

let array1 = [| 1; 2; 3 |]

각 요소를 각기 다른 줄에 입력할 수도 있습니다. 이 경우 구분 기호로 사용되는 세미콜론을 생략할 수 있습니다.

let array1 = 
    [|
        1
        2
        3
     |]

배열 요소의 형식은 사용되는 리터럴을 기준으로 유추됩니다. 모든 요소의 형식에는 일관성이 있어야 합니다. 다음 코드에서는 오류가 발생합니다. 1.0은 부동 소수점인 반면 2와 3은 정수이기 때문입니다.

// Causes an error.
// let array2 = [| 1.0; 2; 3 |] 

시퀀스 식을 사용하여 배열을 만들 수도 있습니다. 다음 예제에서는 1부터 10까지의 정수의 제곱으로 이루어진 배열을 만듭니다.

let array3 = [| for i in 1 .. 10 -> i * i |]

모든 요소가 0으로 초기화된 배열을 만들려면 Array.zeroCreate를 사용합니다.

let arrayOfTenZeroes : int array = Array.zeroCreate 10

요소에 액세스

점 연산자(.) 및 대괄호([ 및 ])를 사용하여 배열 요소에 액세스할 수 있습니다.

array1.[0]

배열 인덱스는 0부터 시작합니다.

조각 표기법을 사용하여 배열 요소에 액세스할 수도 있습니다. 이 경우 배열의 하위 범위를 지정할 수 있습니다. 아래에는 조각 표기법의 예가 나와 있습니다.

// Accesses elements from 0 to 2.
array1.[0..2]  
// Accesses elements from the beginning of the array to 2.
array1.[..2] 
// Accesses elements from 2 to the end of the array.
array1.[2..] 

조각 표기법을 사용하면 배열의 새 복사본이 만들어집니다.

배열 형식과 모듈

모든 F# 배열의 형식은 .NET Framework 형식인 Array입니다. 따라서 F# 배열은 Array에 사용 가능한 모든 기능을 지원합니다.

라이브러리 모듈 Microsoft.FSharp.Collections.Array는 1차원 배열에 대한 작업을 지원합니다. 모듈 Array2D, Array3DArray4D에는 각각 2차원, 3차원 및 4차원 배열에 대한 작업을 지원하는 함수가 있습니다. Array를 사용하여 4차원보다 큰 배열을 만들 수 있습니다.

간단한 함수

Array.get은 요소를 가져옵니다. Array.length는 배열의 길이를 지정합니다. Array.set은 요소를 지정된 값으로 설정합니다. 다음 코드 예제에서는 이러한 함수를 사용하는 방법을 보여 줍니다.

let array1 = Array.create 10 ""
for i in 0 .. array1.Length - 1 do
    Array.set array1 i (i.ToString())
for i in 0 .. array1.Length - 1 do
    printf "%s " (Array.get array1 i)

출력은 다음과 같습니다.

0 1 2 3 4 5 6 7 8 9

배열을 만드는 함수

여러 가지 함수를 사용하여 기존 배열 없이 새 배열을 만들 수 있습니다. Array.empty는 요소가 전혀 포함되어 있지 않은 새 배열을 만듭니다. Array.create는 지정된 크기의 배열을 만들고 모든 요소를 지정된 값으로 설정합니다. Array.init은 요소를 생성하기 위해 지정한 차원 및 함수를 사용하여 배열을 만듭니다. Array.zeroCreate는 모든 요소가 배열 형식의 0에 해당하는 값으로 초기화된 배열을 만듭니다. 다음 코드에서는 이러한 함수를 보여 줍니다.

let myEmptyArray = Array.empty
printfn "Length of empty array: %d" myEmptyArray.Length

printfn "Array of floats set to 5.0: %A" (Array.create 10 5.0)
printfn "Array of squares: %A" (Array.init 10 (fun index -> index * index))
let (myZeroArray : float array) = Array.zeroCreate 10

출력은 다음과 같습니다.

Length of empty array: 0
Area of floats set to 5.0: [|5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0|]
Array of squares: [|0; 1; 4; 9; 16; 25; 36; 49; 64; 81|]

Array.copy는 기존 배열에서 복사한 요소가 들어 있는 새 배열을 만듭니다. 이 복사는 단순 복사입니다. 즉, 요소 형식이 참조 형식이면 참조만 복사되고 내부 개체는 복사되지 않습니다. 다음 코드 예제에서는 그 구체적인 방법을 보여 줍니다.

open System.Text

let firstArray : StringBuilder array = Array.init 3 (fun index -> new StringBuilder(""))
let secondArray = Array.copy firstArray
// Reset an element of the first array to a new value.
firstArray.[0] <- new StringBuilder("Test1")
// Change an element of the first array.
firstArray.[1].Insert(0, "Test2") |> ignore
printfn "%A" firstArray
printfn "%A" secondArray

이 코드는 다음과 같이 출력됩니다.

[|Test1; Test2; |]
[|; Test2; |]

문자열 Test1은 첫째 배열에만 표시됩니다. 새 요소를 만드는 작업 과정에서 firstArray의 참조를 덮어쓰게 되지만 secondArray에 계속 남아 있는 빈 문자열에 대한 원래 참조는 영향을 받지 않기 때문입니다. 문자열 Test2는 두 배열에 모두 표시됩니다. StringBuilder 형식에 대한 Insert 작업이 두 배열에서 모두 참조하는 내부 StringBuilder 개체에 영향을 주기 때문입니다.

Array.sub는 배열의 하위 범위로부터 새 배열을 생성합니다. 시작 인덱스와 길이를 설정하여 하위 범위를 지정합니다. 다음 코드에서는 Array.sub를 사용하는 방법을 보여 줍니다.

let a1 = [| 0 .. 99 |]
let a2 = Array.sub a1 5 10
printfn "%A" a2

출력 결과로는 요소 5부터 시작하여 요소 10개를 포함하는 하위 범위가 표시됩니다.

[|5; 6; 7; 8; 9; 10; 11; 12; 13; 14|]

Array.append는 기존의 배열 두 개를 결합하여 새 배열을 만듭니다.

다음 코드에서는 Array.append를 사용하는 방법을 보여 줍니다.

printfn "%A" (Array.append [| 1; 2; 3|] [| 4; 5; 6|])

이 코드는 다음과 같이 출력됩니다.

[|1; 2; 3; 4; 5; 6|]

Array.choose는 배열의 요소를 선택하여 새 배열에 포함합니다. 다음 코드에서는 Array.choose를 사용하는 방법을 보여 줍니다. 배열의 요소 형식은 옵션 형식에 반환되는 값의 형식과 같지 않아도 됩니다. 이 예제에서 요소 형식은 int이고 옵션은 다항 함수 elem*elem - 1의 결과인 부동 소수점 숫자입니다.

printfn "%A" (Array.choose (fun elem -> if elem % 2 = 0 then
                                            Some(float (elem*elem - 1))
                                        else
                                            None) [| 1 .. 10 |])

이 코드는 다음과 같이 출력됩니다.

[|3.0; 15.0; 35.0; 63.0; 99.0|]

Array.collect는 기존 배열의 각 배열 요소에 대해 지정된 함수를 실행한 다음 함수를 통해 생성된 요소를 수집하고 이를 결합하여 새 배열을 만듭니다. 다음 코드에서는 Array.collect를 사용하는 방법을 보여 줍니다.

printfn "%A" (Array.collect (fun elem -> [| 0 .. elem |]) [| 1; 5; 10|])

이 코드는 다음과 같이 출력됩니다.

[|0; 1; 0; 1; 2; 3; 4; 5; 0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]

Array.concat는 배열의 시퀀스를 결합하여 단일 배열을 만듭니다. 다음 코드에서는 Array.concat를 사용하는 방법을 보여 줍니다.

let multiplicationTable max = seq { for i in 1 .. max -> [| for j in 1 .. max -> (i, j, i*j) |] }
printfn "%A" (Array.concat (multiplicationTable 3))

이 코드는 다음과 같이 출력됩니다.

[|(1, 1, 1); (1, 2, 2); (1, 3, 3); (2, 1, 2); (2, 2, 4); (2, 3, 6); (3, 1, 3);
  (3, 2, 6); (3, 3, 9)|]

Array.filter는 부울 조건 함수를 사용하여 입력 배열 중 조건이 참인 요소로만 이루어진 새 배열을 생성합니다. 다음 코드에서는 Array.filter를 사용하는 방법을 보여 줍니다.

printfn "%A" (Array.filter (fun elem -> elem % 2 = 0) [| 1 .. 10|])

이 코드는 다음과 같이 출력됩니다.

[|2; 4; 6; 8; 10|]

Array.rev는 기존 배열의 순서를 뒤집어 새 배열을 생성합니다. 다음 코드에서는 Array.rev를 사용하는 방법을 보여 줍니다.

let stringReverse (s: string) =
    System.String(Array.rev (s.ToCharArray()))

printfn "%A" (stringReverse("!dlrow olleH"))

이 코드는 다음과 같이 출력됩니다.

"Hello world!"

다음 예제에서와 같이 파이프라인 연산자(|>)를 사용하여 배열 모듈에서 배열을 변환하는 함수를 쉽게 결합할 수 있습니다.

[| 1 .. 10 |]
|> Array.filter (fun elem -> elem % 2 = 0)
|> Array.choose (fun elem -> if (elem <> 8) then Some(elem*elem) else None)
|> Array.rev
|> printfn "%A"

다음과 같이 출력됩니다.

[|100; 36; 16; 4|]

다차원 배열

다차원 배열을 만들 수 있지만 다차원 배열 리터럴을 작성하기 위한 별도의 구문은 없습니다. 배열 요소의 시퀀스로 이루어진 시퀀스로부터 배열을 만들려면 array2D 연산자를 사용합니다. 시퀀스는 배열 또는 목록 리터럴일 수 있습니다. 예를 들어 다음 코드에서는 2차원 배열을 만듭니다.

let my2DArray = array2D [ [ 1; 0]; [0; 1] ]

Array2D.init 함수를 사용하여 2차원 배열을 초기화할 수도 있습니다. 3차원 및 4차원 배열에 대해서도 같은 기능을 수행하는 함수가 있습니다. 이들 함수는 요소를 만드는 데 사용되는 함수를 취합니다. 함수를 지정하는 대신 초기 값으로 설정된 요소가 들어 있는 2차원 배열을 만들려면 Array2D.create 함수를 사용합니다. 3차원 및 4차원 배열에도 이 함수를 사용할 수 있습니다. 다음 코드 예제에서는 필요한 요소의 배열로 이루어진 배열을 만든 다음 Array2D.init을 사용하여 2차원 배열을 생성하는 방법을 보여 줍니다.

let arrayOfArrays = [| [| 1.0; 0.0 |]; [|0.0; 1.0 |] |]
let twoDimensionalArray = Array2D.init 2 2 (fun i j -> arrayOfArrays.[i].[j]) 

배열 인덱스 및 조각 구문은 최대 4차원 배열까지 지원됩니다. 다차원의 인덱스를 지정할 때는 다음 코드 예제에서와 같이 쉼표를 사용하여 인덱스를 구분합니다.

twoDimensionalArray.[0, 1] <- 1.0

2차원 배열의 형식은 int[,] 또는 double[,]의 예에서와 같이 <type>[,]으로 작성되고 3차원 배열의 형식은 <type>[,,]으로 작성됩니다. 더 높은 차원의 배열에 대해서도 같은 방식이 적용됩니다.

1차원 배열에 사용되는 함수 중 일부만 다차원 배열에도 사용할 수 있습니다. 자세한 내용은 Collections.Array 모듈(F#), Collections.Array2D 모듈(F#), Collections.Array3D 모듈(F#)Collections.Array4D 모듈(F#)을 참조하십시오.

배열에 대한 부울 함수

Array.existsArray.exists2 함수는 각각 배열 한 개 또는 두 개의 요소를 테스트합니다. 이들 함수는 테스트 함수를 사용하며, 조건을 충족하는 요소(Array.exists2의 경우 요소 쌍)가 있으면 true를 반환합니다.

다음 코드에서는 Array.existsArray.exists2를 사용하는 방법을 보여 줍니다. 이들 예제에서는 인수 중 하나(이 경우 함수 인수)만 적용하여 새 함수를 만듭니다.

let allNegative = Array.exists (fun elem -> abs (elem) = elem) >> not
printfn "%A" (allNegative [| -1; -2; -3 |])
printfn "%A" (allNegative [| -10; -1; 5 |])
printfn "%A" (allNegative [| 0 |])
let haveEqualElement = Array.exists2 (fun elem1 elem2 -> elem1 = elem2)
printfn "%A" (haveEqualElement [| 1; 2; 3 |] [| 3; 2; 1|])

이 코드는 다음과 같이 출력됩니다.

true
false
false
true

마찬가지로, Array.forall 함수는 배열을 테스트하여 모든 요소가 부울 조건을 충족하는지 확인합니다. 이 함수의 변형인 Array.forall2는 길이가 같은 두 배열의 요소에 대해 부울 함수를 사용하여 동일한 작업을 수행합니다. 다음 코드에서는 이러한 함수를 사용하는 방법을 보여 줍니다.

let allPositive = Array.forall (fun elem -> elem > 0)
printfn "%A" (allPositive [| 0; 1; 2; 3 |])
printfn "%A" (allPositive [| 1; 2; 3 |])
let allEqual = Array.forall2 (fun elem1 elem2 -> elem1 = elem2)
printfn "%A" (allEqual [| 1; 2 |] [| 1; 2 |])
printfn "%A" (allEqual [| 1; 2 |] [| 2; 1 |])

이 예제는 다음과 같이 출력됩니다.

false
true
true
false

배열 검색

Array.find는 부울 함수를 사용하여 함수의 반환 결과가 처음으로 true가 되는 요소를 반환합니다. 조건을 충족하는 요소를 찾지 못하면 KeyNotFoundException이 발생합니다. Array.findIndexArray.find와 비슷하지만 요소 자체를 반환하는 것이 아니라 요소의 인덱스를 반환한다는 점에서 차이가 있습니다.

다음 코드에서는 Array.findArray.findIndex를 사용하여 완전한 정사각형이면서 완전한 입방체인 수를 찾습니다.

let arrayA = [| 2 .. 100 |]
let delta = 1.0e-10
let isPerfectSquare (x:int) =
    let y = sqrt (float x)
    abs(y - round y) < delta
let isPerfectCube (x:int) =
    let y = System.Math.Pow(float x, 1.0/3.0)
    abs(y - round y) < delta
let element = Array.find (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
let index = Array.findIndex (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
printfn "The first element that is both a square and a cube is %d and its index is %d." element index

출력은 다음과 같습니다.

The first element that is both a square and a cube is 64 and its index is 62.

Array.tryFindArray.find와 비슷하지만 해당 결과가 옵션 형식이라는 점에서 차이가 있습니다. 조건을 충족하는 요소를 찾지 못하면 None이 반환됩니다. 일치하는 요소가 배열에 있는지 여부를 모를 때는 Array.find 대신 Array.tryFind를 사용해야 합니다. 마찬가지로, Array.tryFindIndexArray.findIndex와 비슷하지만 옵션 형식이 반환 값이라는 점에서 차이가 있습니다. 요소를 찾지 못한 경우 옵션은 None입니다.

다음 코드에서는 Array.tryFind를 사용하는 방법을 보여 줍니다. 이 코드는 위의 코드를 전제로 합니다.

let delta = 1.0e-10
let isPerfectSquare (x:int) =
    let y = sqrt (float x)
    abs(y - round y) < delta
let isPerfectCube (x:int) =
    let y = System.Math.Pow(float x, 1.0/3.0)
    abs(y - round y) < delta
let lookForCubeAndSquare array1 =
    let result = Array.tryFind (fun elem -> isPerfectSquare elem && isPerfectCube elem) array1
    match result with
    | Some x -> printfn "Found an element: %d" x
    | None -> printfn "Failed to find a matching element."

lookForCubeAndSquare [| 1 .. 10 |]
lookForCubeAndSquare [| 100 .. 1000 |]
lookForCubeAndSquare [| 2 .. 50 |]

출력은 다음과 같습니다.

Found an element: 1
Found an element: 729

요소를 찾기만 하는 것이 아니라 변환까지 해야 한다면 Array.tryPick를 사용합니다. 이 함수를 실행하면 변환된 요소가 옵션 값으로 반환되는 첫째 요소를 결과로 얻을 수 있습니다. 여기에 해당하는 요소를 찾지 못하면 None이 반환됩니다.

다음 코드에서는 Array.tryPick을 사용하는 방법을 보여 줍니다. 여기서는 코드를 간소화하기 위해 람다 식 대신 여러 개의 도우미 함수를 정의합니다.

let findPerfectSquareAndCube array1 =
    let delta = 1.0e-10
    let isPerfectSquare (x:int) =
        let y = sqrt (float x)
        abs(y - round y) < delta
    let isPerfectCube (x:int) =
        let y = System.Math.Pow(float x, 1.0/3.0)
        abs(y - round y) < delta
    // intFunction : (float -> float) -> int -> int
    // Allows the use of a floating point function with integers.
    let intFunction function1 number = int (round (function1 (float number)))
    let cubeRoot x = System.Math.Pow(x, 1.0/3.0)
    // testElement: int -> (int * int * int) option
    // Test an element to see whether it is a perfect square and a perfect
    // cube, and, if so, return the element, square root, and cube root
    // as an option value. Otherwise, return None.
    let testElement elem = 
        if isPerfectSquare elem && isPerfectCube elem then
            Some(elem, intFunction sqrt elem, intFunction cubeRoot elem)
        else None
    match Array.tryPick testElement array1 with
    | Some (n, sqrt, cuberoot) -> printfn "Found an element %d with square root %d and cube root %d." n sqrt cuberoot
    | None -> printfn "Did not find an element that is both a perfect square and a perfect cube."

findPerfectSquareAndCube [| 1 .. 10 |]
findPerfectSquareAndCube [| 2 .. 100 |]
findPerfectSquareAndCube [| 100 .. 1000 |]
findPerfectSquareAndCube [| 1000 .. 10000 |]
findPerfectSquareAndCube [| 2 .. 50 |]

출력은 다음과 같습니다.

Found an element 1 with square root 1 and cube root 1.
Found an element 64 with square root 8 and cube root 4.
Found an element 729 with square root 27 and cube root 9.
Found an element 4096 with square root 64 and cube root 16.

배열에 대한 계산 수행

Array.average 함수는 배열의 각 요소에 대한 평균을 반환합니다. 이 함수는 정수로 정확하게 나누어 떨어지는 요소 형식에만 사용할 수 있습니다. 여기에 부동 소수점 형식은 포함되지만 정수 형식은 포함되지 않습니다. Array.averageBy 함수는 각 요소에 대해 함수를 호출한 결과의 평균을 반환합니다. 정수 형식으로 이루어진 배열에 대해서는 Array.averageBy를 사용하고 각 요소를 부동 소수점 형식으로 변환하는 함수를 사용하여 계산을 수행할 수 있습니다.

요소 형식에서 지원하는 경우 Array.max 또는 Array.min을 사용하여 최대값 또는 최소값 요소를 구할 수 있습니다. 마찬가지로, Array.maxByArray.minBy를 사용하면 비교를 지원하는 형식으로 요소를 변환하는 함수를 먼저 실행할 수 있습니다.

Array.sum은 배열의 요소를 더하고, Array.sumBy는 각 요소에 대해 함수를 호출하여 그 결과를 함께 더합니다.

반환 값을 저장하지 않은 채 배열의 각 요소에 대해 함수를 실행하려면 Array.iter을 사용합니다. 길이가 같은 배열 두 개가 함수에 필요하면 Array.iter2를 사용합니다. 함수의 결과로 이루어진 배열을 유지해야 하는 경우에는 Array.map 또는 한꺼번에 배열 두 개를 대상으로 삼아 실행되는 Array.map2를 사용합니다.

변형된 함수인 Array.iteriArray.iteri2를 사용하면 요소의 인덱스를 계산 대상으로 삼을 수 있습니다. Array.mapiArray.mapi2의 경우도 마찬가지입니다.

Array.fold, Array.foldBack, Array.reduce, Array.reduceBack, Array.scanArray.scanBack 함수는 배열의 모든 요소를 대상으로 하는 알고리즘을 실행합니다. 마찬가지로, 변형된 함수인 Array.fold2Array.foldBack2는 두 배열을 대상으로 계산을 수행합니다.

계산을 수행하기 위한 이들 함수는 List 모듈에 있는 같은 이름의 함수에 상응합니다. 사용 방법의 예제는 목록(F#)을 참조하십시오.

배열 수정

Array.set은 요소를 지정된 값으로 설정합니다. Array.fill은 배열의 요소 범위를 지정된 값으로 설정합니다. 다음 코드에서는 Array.fill의 예를 보여 줍니다.

let arrayFill1 = [| 1 .. 25 |]
Array.fill arrayFill1 2 20 0
printfn "%A" arrayFill1

출력은 다음과 같습니다.

[|1; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 23; 24; 25|]

Array.blit를 사용하여 한 배열의 하위 섹션을 다른 배열로 복사할 수 있습니다.

형식 사이의 변환

Array.ofList는 목록으로부터 배열을 만듭니다. Array.ofSeq는 시퀀스로부터 배열을 만듭니다. Array.toListArray.toSeq는 배열 형식을 함수 이름에 나오는 다른 컬렉션 형식으로 변환합니다.

배열 정렬

제네릭 비교 함수로 배열을 정렬하려면 Array.sort를 사용합니다. Array.sortBy를 사용하면 키라고 하는 값을 생성하는 함수를 지정하고 키에 대해 제네릭 비교 함수를 사용하여 배열을 정렬할 수 있습니다. 사용자 지정 비교 함수가 필요하면 Array.sortWith를 사용합니다. Array.sort, Array.sortByArray.sortWith는 모두 정렬된 배열을 새 배열로 반환합니다. 변형된 함수인 Array.sortInPlace, Array.sortInPlaceByArray.sortInPlaceWith는 새 배열을 반환하는 대신 기존 배열을 수정합니다.

배열과 튜플

Array.zipArray.unzip 함수는 튜플 쌍으로 이루어진 배열을 배열로 이루어진 튜플로 변환하거나 그 반대로 변환합니다. Array.zip3Array.unzip3은 요소가 세 개인 튜플이나 배열 세 개로 이루어진 튜플을 대상으로 작동한다는 점을 제외하고는 기능이 같습니다.

배열에 대한 병렬 계산

Array.Parallel 모듈에는 배열에 대한 병렬 계산을 수행하는 함수가 포함되어 있습니다. 버전 4 이전의 .NET Framework 버전을 대상으로 하는 응용 프로그램에는 이 모듈을 사용할 수 없습니다.

참고 항목

기타 리소스

F# 언어 참조

F# 형식