Вложенные функции и области видимости

Последнее обновление: 10.12.2023

В языке F# функции могут быть вложенными, то есть располагаться внутри других функций. Например:

let outer() = 
    let inner() = printfn "Inner scope"
    printfn "Outer scope"

В данном случае внутри функции outer определена функция inner.

Однако здесь функция inner хотя и определена, но не вызывается. Даже если мы вызовем функцию outer:

let outer() = 
    let inner() = printfn "Inner scope"
    printfn "Outer scope"

outer()		// вызываем функцию outer

Функция inner не будет автоматически выполняться. Для выполнения ее надо вызвать в функции outer:

let outer() = 
    let inner() = printfn "Inner scope"
    inner()     // вызываем функцию inner
    printfn "Outer scope"

outer()     // вызываем функцию outer

Область видимости

Программа на языке F# можно иметь различные области видимости (scope), одна область видимости может располагаться внутри другой и таким образом образовывать различные уровни. Каждая отдельная функциия образует свою область видимости. Например:

// глобальная область видимости
printfn "Global scope"

let outer() =       // область видимости функции outer
    let inner() =              // область видимости функции inner
        printfn "Inner scope"   

    printfn "Outer scope"

Здесь мы сталкиваемся с тремя областями видимости или контекстами. Вне функций расположена глобальная область видимости. Именно в ней определена функция outer.

Функция outer определяет вложенную свою область видимости. Именно в этой области видимости определена функция inner.

Функция inner определяет вложенную по отношению к функции outer область видимости.

За счет вложенных функций мы можем создавать все более глубокие уровни областей видимостей:

// глобальная область видимости
printfn "Global scope"

let outer() = 			// область видимости outer
    let inner() = 				// область видимости inner
        let subinner() = 			// область видимости subinner
            printfn "Subinner scope"
        printfn "Inner scope"

    printfn "Outer scope"

Но из этого следует ограничение: все значения и функции доступны только в рамках той области видимости, где они определены или во вложенных областях видимости, но НЕ во внешних. Например:

// глобальная область видимости

let a = 5
let helloGlobal() = printfn "Global scope"

let outer() =       // область видимости функции outer

    let inner() =              // область видимости функции inner
        helloGlobal()         // обращение к фунции helloGlobal из глобального контекста
        printfn $"Inner scope. a: {a}"      // обращение к значению a из глобального контекста

    inner()
    printfn "Outer scope"

outer()

Здесь на глобальном уровне определено одно значение a и две функции helloGlobal и outer. В любом месте глобальной области видимости, в том числе в функции outer и ее вложенных контекстах мы можем обратиться к глобальным значениям и функциям.

Другой пример: попробуем обратиться к значениям и функциям, определенным внутри другой функции:

let outer() =       // область видимости функции outer

    let helloOuter() = printfn "Outer scope"
    let b = 15

    let inner() =              // область видимости функции inner
        helloOuter()         // обращение к фунции helloOuter из контекста outer
        printfn $"Inner scope. b: {b}"      // обращение к значению b из контекста outer

    inner()


// глобальная область видимости
outer()
// helloOuter()         //  функция helloOuter из контекста outer недоступна на глобальном контексте
// printfn $"Global scope. b: {b}"      // значение b из контекста outer недоступно на глобальном контексте

Здесь в функции outer определено одно значение b и две функции helloOuter и inner. В любом месте функции outer, в том числе ее вложенных контекстах мы можем обратиться к ее значениям и функциям. Однако вне функции outer определенные внутри нее значения и функции будут не доступны:

// глобальная область видимости
outer()
// helloOuter()         //  функция helloOuter из контекста outer недоступна на глобальном контексте
// printfn $"Global scope. b: {b}"      // значение b из контекста outer недоступно на глобальном контексте

Соответственно все значения и функции, определенные внутри функции inner, были бы доступны только в рамках этой функции.

Скрытие значений и функций

Внутри внутренней области видимости F# позволяет задавать значения и функции с теми же именами, что и во внешней области видимости.

// глобальная область видимости
let n = 10
let hello() = printfn $"Global scope. n = {n}"

let outer() =       // область видимости функции outer

    let n = 50 
    let hello() = printfn $"Outer scope. n = {n}"

    let inner() =              // область видимости функции inner
        let n = 250
        let hello() = printfn $"Inner scope. n = {n}"
        hello()     // Inner scope. n = 250

    inner()    // Inner scope. n = 250
    hello()    // Outer scope. n = 50


outer() 

hello()     //  Global scope. n = 10

Консольный вывод программы:

Inner scope. n = 250
Outer scope. n = 50
Global scope. n = 10

Здесь на глобальном уровне определены значение n и функция hello:

let n = 10
let hello() = printfn $"Global scope. n = {n}"

На уровне функции outer определяются те же значение и функция:

let n = 50 
let hello() = printfn $"Outer scope. n = {n}"

Далее все обращения к значению n и функции hello будут использовать их определения на уровне функции outer, а не из глобальной области видимости. То есть значение n из функции outer скрывает значение n из глобального контекста, а определение функции hello скрывает определение этой функции из глобального контекста.

Но далее во вложенной функции inner также определяются значение и функция с теми же именами:

let n = 250
let hello() = printfn $"Inner scope. n = {n}"

Подобным образом определение значения n и функции hello из функции inner скрывают определения значение n и функции hello из контекста функции outer. Поэтому далее внутри функции inner будут использоваться определения этих значения и функции из функции inner, а не из функции outer

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850