다음을 통해 공유


참조 셀(F#)

참조 셀은 참조 의미론을 통해 변경 가능한 값을 만드는 데 사용할 수 있는 저장 위치입니다.

ref expression

설명

값 앞에 ref 연산자를 사용하여 값을 캡슐화하는 새 참조 셀을 만들 수 있습니다. 이는 변경 가능한 변수이므로 참조 셀을 만든 다음 내부 값을 변경할 수 있습니다.

참조 셀은 단순한 주소가 아니라 실제 값을 저장하는 위치입니다. ref 연산자를 사용하여 참조 셀을 만들면 캡슐화되고 변경 가능한 값으로 내부 값의 복사본이 만들어집니다.

!(느낌표) 연산자를 사용하여 참조 셀을 역참조할 수 있습니다.

다음 코드 예제에서는 참조 셀을 선언하고 사용하는 방법을 보여 줍니다.

// Declare a reference.
let refVar = ref 6

// Change the value referred to by the reference.
refVar := 50

// Dereference by using the ! operator.
printfn "%d" !refVar

출력은 50입니다.

참조 셀은 다음과 같이 선언되는 Ref 제네릭 레코드 형식의 인스턴스입니다.

type Ref<'a> =
    { mutable contents: 'a }

형식 'a ref는 Ref<'a>와 같은 의미입니다. IDE의 컴파일러와 IntelliSense에서는 이 형식을 전자와 같이 표시하지만 그 내부 정의는 후자와 같습니다.

ref 연산자는 새 참조 셀을 만드는 데 사용됩니다. 다음 코드는 ref 연산자의 선언입니다.

let ref x = { contents = x }

다음 표에는 참조 셀에 사용할 수 있는 기능이 나와 있습니다.

연산자, 멤버 또는 필드

설명

형식

내용

!(역참조 연산자)

내부 값을 반환합니다.

'a ref -> 'a

let (!) r = r.contents

:=(할당 연산자)

내부 값을 변경합니다.

'a ref -> 'a -> unit

let (:=) r x = r.contents <- x

ref(연산자)

값을 새 참조 셀로 캡슐화합니다.

'a -> 'a ref

let ref x = { contents = x }

Value(속성)

내부 값을 가져오거나 설정합니다.

unit -> 'a

member x.Value = x.contents

contents(레코드 필드)

내부 값을 가져오거나 설정합니다.

'a

let ref x = { contents = x }

내부 값에 액세스하는 데는 여러 가지 방법이 있습니다. 역참조 연산자(!)를 통해 반환되는 값은 할당 가능한 값이 아닙니다. 따라서 내부 값을 수정하는 경우에는 할당 연산자(:=)를 대신 사용해야 합니다.

Value 속성과 contents 필드는 둘 다 할당 가능한 값입니다. 따라서 다음 코드에서와 같이 이들 속성과 필드를 사용하여 내부 값에 액세스하거나 내부 값을 변경할 수 있습니다.

let xRef : int ref = ref 10

printfn "%d" (xRef.Value)
printfn "%d" (xRef.contents)

xRef.Value <- 11
printfn "%d" (xRef.Value)
xRef.contents <- 12
printfn "%d" (xRef.contents)

출력은 다음과 같습니다.

10
10
11
12

필드 contents는 다른 ML 버전과의 호환성을 위해 제공되며 컴파일 과정에서 이 필드로 인해 경고가 발생합니다. 경고가 발생하지 않도록 하려면 --mlcompatibility 컴파일러 옵션을 사용해야 합니다. 자세한 내용은 컴파일러 옵션(F#)을 참조하십시오.

예제

다음 코드에서는 매개 변수 전달에 참조 셀을 사용하는 방법을 보여 줍니다. Incrementor 형식에는 Increment 메서드가 있습니다. 이 메서드는 매개 변수 형식에 byref를 포함하는 매개 변수를 취합니다. 매개 변수 형식의 byref는 호출자가 지정된 형식의 일반적인 변수에 대한 주소 또는 참조 셀을 전달해야 함을 의미합니다. 이 예제의 경우 지정된 형식은 int입니다. 코드의 나머지 부분에서는 두 가지 인수 형식을 모두 사용하여 Increment를 호출하는 방법을 보여 주고, 변수에 대해 ref 연산자를 사용하여 참조 셀(ref myDelta1)을 만드는 방법을 보여 줍니다. 그런 다음 이 예제에서는 주소 연산자(&)를 사용하여 적절한 인수를 생성하는 방법을 보여 줍니다. 마지막으로 let 바인딩을 사용하여 선언한 참조 셀을 통해 Increment 메서드를 다시 호출합니다. 코드의 마지막 줄에서는 ! 연산자를 사용하여 인쇄할 참조 셀을 역참조하는 방법을 보여 줍니다.

type Incrementor(delta) =
    member this.Increment(i : int byref) =
        i <- i + delta

let incrementor = new Incrementor(1)
let mutable myDelta1 = 10
incrementor.Increment(ref myDelta1)
// Prints 10:
printfn "%d" myDelta1  

let mutable myDelta2 = 10
incrementor.Increment(&myDelta2)
// Prints 11:
printfn "%d" myDelta2 

let refInt = ref 10
incrementor.Increment(refInt)
// Prints 11:
printfn "%d" !refInt

참조를 통한 전달 방법에 대한 자세한 내용은 매개 변수 및 인수(F#)을 참조하십시오.

참고

C# 프로그래머는 F#에서 ref가 작동하는 방식이 C#의 경우와 다르다는 점에 유의해야 합니다. 예를 들어 인수를 전달할 때 ref를 사용하는 경우 F#에서의 결과와 C#에서의 결과가 서로 다릅니다.

참조 셀 및변경할 수 있는 변수

참조 셀과 변경 가능한 변수는 대개의 경우 동일한 상황에서 사용할 수 있습니다. 그러나 상황에 따라서는 변경 가능한 변수를 사용할 수 없기 때문에 참조 셀을 대신 사용해야 하는 경우도 있습니다. 일반적으로 컴파일러에서 허용하는 경우라면 변경 가능한 변수를 사용하는 것이 좋습니다. 그러나 클로저를 생성하는 식에서는 변경 가능한 변수를 사용할 수 없다는 메시지가 컴파일러를 통해 생성됩니다. 클로저는 람다 식, 시퀀스 식, 계산 식, 부분적으로 적용된 인수를 사용하는 변환된 함수 등의 특정 F# 식을 통해 생성되는 로컬 함수입니다. 이러한 식을 통해 생성된 클로저는 나중에 계산할 수 있도록 저장됩니다. 변경 가능한 변수에 대해서는 이 프로세스를 진행할 수 없습니다. 따라서 이와 같은 식에 변경 가능 상태가 필요하면 참조 셀을 사용해야 합니다. 클로저에 대한 자세한 내용은 클로저(F#) 항목을 참조하십시오.

다음 코드 예제에서는 참조 셀을 사용해야 하는 시나리오를 보여 줍니다.

// Print all the lines read in from the console.
let PrintLines1() =
    let mutable finished = false
    while not finished do
        match System.Console.ReadLine() with
        | null -> finished <- true
        | s -> printfn "line is: %s" s


// Attempt to wrap the printing loop into a 
// sequence expression to delay the computation.
let PrintLines2() =
    seq {
        let mutable finished = false
        // Compiler error:
        while not finished do  
            match System.Console.ReadLine() with
            | null -> finished <- true
            | s -> yield s
    }

// You must use a reference cell instead.
let PrintLines3() =
    seq {
        let finished = ref false
        while not !finished do
            match System.Console.ReadLine() with
            | null -> finished := true
            | s -> yield s
    }

위 코드에서는 참조 셀 finished가 로컬 상태에 포함되어 있습니다. 즉, 클로저에 있는 변수가 전적으로 식 내에서만 작성 및 사용됩니다. 이 예제의 경우 변수를 만들고 사용하는 식은 시퀀스 식입니다. 여기서 변수가 로컬이 아니라면 어떤 결과가 발생할지 생각해 볼 필요가 있습니다. 클로저에서 로컬이 아닌 상태에도 액세스할 수 있지만 이 경우 변수가 값으로 복사 및 저장됩니다. 이러한 프로세스를 값 의미론이라고 합니다. 이는 복사 시점에서의 값이 저장되며 변수에 대한 이후의 변경 내용은 전혀 반영되지 않음을 뜻합니다. 로컬이 아닌 변수의 변경 내용을 추적하려면, 즉 클로저에서 참조 의미론을 사용하여 로컬이 아닌 상태를 처리해야 하는 경우에는 참조 셀을 사용해야 합니다.

다음 코드 예제에서는 클로저에 참조 셀을 사용하는 방법을 보여 줍니다. 여기서 클로저는 함수 인수를 부분 적용하여 얻은 결과입니다.

// The following code demonstrates the use of reference
// cells to enable partially applied arguments to be changed
// by later code.

let increment1 delta number = number + delta

let mutable myMutableIncrement = 10

// Closures created by partial application and literals.
let incrementBy1 = increment1 1
let incrementBy2 = increment1 2

// Partial application of one argument from a mutable variable.
let incrementMutable = increment1 myMutableIncrement

myMutableIncrement <- 12

// This line prints 110.
printfn "%d" (incrementMutable 100)

let myRefIncrement = ref 10

// Partial application of one argument, dereferenced
// from a reference cell.
let incrementRef = increment1 !myRefIncrement

myRefIncrement := 12

// This line also prints 110.
printfn "%d" (incrementRef 100)

// Reset the value of the reference cell.
myRefIncrement := 10

// New increment function takes a reference cell.
let increment2 delta number = number + !delta

// Partial application of one argument, passing a reference cell
// without dereferencing first.
let incrementRef2 = increment2 myRefIncrement

myRefIncrement := 12

// This line prints 112.
printfn "%d" (incrementRef2 100)

참고 항목

참조

기호 및 연산자 참조(F#)

개념

매개 변수 및 인수(F#)

기타 리소스

F# 언어 참조