Функции первого классаFirst-class functions

Определение характеристик языков функционального программирования — это повышение уровня функций до состояния первого класса.A defining characteristic of functional programming languages is the elevation of functions to first-class status. Вы должны иметь возможность выполнять функции, которые можно использовать со значениями других встроенных типов, и иметь возможность сделать это с сравнимой степенью усилий.You should be able to do with a function whatever you can do with values of the other built-in types, and be able to do so with a comparable degree of effort.

К типичным мерам состояния первого класса относятся следующие:Typical measures of first-class status include the following:

  • Можно ли привязать функции к идентификаторам?Can you bind functions to identifiers? То есть можно ли присвоить им имена?That is, can you give them names?

  • Можно ли хранить функции в структурах данных, например в списке?Can you store functions in data structures, such as in a list?

  • Можно ли передать функцию в качестве аргумента в вызове функции?Can you pass a function as an argument in a function call?

  • Можно ли вернуть функцию из вызова функции?Can you return a function from a function call?

Последние две меры определяют, что называются операциями высшего порядка или функциями высшего порядка.The last two measures define what are known as higher-order operations or higher-order functions. Функции высшего порядка принимают функции в качестве аргументов и возвращают функции в качестве значений вызовов функций.Higher-order functions accept functions as arguments and return functions as the value of function calls. Эти операции поддерживают такие основ функционального программирования, как функции сопоставления и компоновка функций.These operations support such mainstays of functional programming as mapping functions and composition of functions.

Присвоить значение имениGive the Value a Name

Если функция является значением первого класса, необходимо иметь возможность присвоить ей имя, точно так же, как целые числа, строки и другие встроенные типы.If a function is a first-class value, you must be able to name it, just as you can name integers, strings, and other built-in types. В литературе по функциональному программированию это называется привязкой идентификатора к значению.This is referred to in functional programming literature as binding an identifier to a value. F # использует let привязки для привязки имен к значениям: let <identifier> = <value> .F# uses let bindings to bind names to values: let <identifier> = <value>. В следующем коде показаны два примера.The following code shows two examples.

// Integer and string.
let num = 10
let str = "F#"

Вы можете легко присвоить имя функции.You can name a function just as easily. В следующем примере определяется функция с именем squareIt путем привязки идентификатора squareIt к лямбда-выражению fun n -> n * n .The following example defines a function named squareIt by binding the identifier squareIt to the lambda expression fun n -> n * n. Функция squareIt имеет один параметр, n и возвращает квадрат этого параметра.Function squareIt has one parameter, n, and it returns the square of that parameter.

let squareIt = fun n -> n * n

F # предоставляет следующий более краткий синтаксис для достижения того же результата с меньшей типизацией.F# provides the following more concise syntax to achieve the same result with less typing.

let squareIt2 n = n * n

В приведенных ниже примерах используется первый стиль, let <function-name> = <lambda-expression> чтобы подчеркнуть сходство между объявлением функций и объявлением других типов значений.The examples that follow mostly use the first style, let <function-name> = <lambda-expression>, to emphasize the similarities between the declaration of functions and the declaration of other types of values. Однако все именованные функции также можно записать с помощью краткого синтаксиса.However, all the named functions can also be written with the concise syntax. Некоторые примеры написаны обоими способами.Some of the examples are written in both ways.

Сохранение значения в структуре данныхStore the Value in a Data Structure

Значение первого класса может храниться в структуре данных.A first-class value can be stored in a data structure. В следующем коде показаны примеры хранения значений в списках и кортежах.The following code shows examples that store values in lists and in tuples.

// Lists.

// Storing integers and strings.
let integerList = [ 1; 2; 3; 4; 5; 6; 7 ]
let stringList = [ "one"; "two"; "three" ]

// You cannot mix types in a list. The following declaration causes a
// type-mismatch compiler error.
//let failedList = [ 5; "six" ]

// In F#, functions can be stored in a list, as long as the functions
// have the same signature.

// Function doubleIt has the same signature as squareIt, declared previously.
//let squareIt = fun n -> n * n
let doubleIt = fun n -> 2 * n

// Functions squareIt and doubleIt can be stored together in a list.
let funList = [ squareIt; doubleIt ]

// Function squareIt cannot be stored in a list together with a function
// that has a different signature, such as the following body mass
// index (BMI) calculator.
let BMICalculator = fun ht wt ->
                    (float wt / float (squareIt ht)) * 703.0

// The following expression causes a type-mismatch compiler error.
//let failedFunList = [ squareIt; BMICalculator ]


// Tuples.

// Integers and strings.
let integerTuple = ( 1, -7 )
let stringTuple = ( "one", "two", "three" )

// A tuple does not require its elements to be of the same type.
let mixedTuple = ( 1, "two", 3.3 )

// Similarly, function elements in tuples can have different signatures.
let funTuple = ( squareIt, BMICalculator )

// Functions can be mixed with integers, strings, and other types in
// a tuple. Identifier num was declared previously.
//let num = 10
let moreMixedTuple = ( num, "two", 3.3, squareIt )

Чтобы убедиться, что имя функции, сохраненное в кортеже, фактически выполняет вычисление функции, в следующем примере используются fst snd операторы и для извлечения первого и второго элементов из кортежа funAndArgTuple .To verify that a function name stored in a tuple does in fact evaluate to a function, the following example uses the fst and snd operators to extract the first and second elements from tuple funAndArgTuple. Первый элемент в кортеже имеет значение squareIt , а второй элемент — num .The first element in the tuple is squareIt and the second element is num. Идентификатор num привязывается в предыдущем примере к целому 10, допустимому аргументу для squareIt функции.Identifier num is bound in a previous example to integer 10, a valid argument for the squareIt function. Второе выражение применяет первый элемент кортежа ко второму элементу в кортеже: squareIt num .The second expression applies the first element in the tuple to the second element in the tuple: squareIt num.

// You can pull a function out of a tuple and apply it. Both squareIt and num
// were defined previously.
let funAndArgTuple = (squareIt, num)

// The following expression applies squareIt to num, returns 100, and
// then displays 100.
System.Console.WriteLine((fst funAndArgTuple)(snd funAndArgTuple))

Точно так же, как идентификатор num и целое число 10, можно использовать взаимозаменяемость, поэтому может быть идентификатором squareIt и лямбда-выражением fun n -> n * n .Similarly, just as identifier num and integer 10 can be used interchangeably, so can identifier squareIt and lambda expression fun n -> n * n.

// Make a tuple of values instead of identifiers.
let funAndArgTuple2 = ((fun n -> n * n), 10)

// The following expression applies a squaring function to 10, returns
// 100, and then displays 100.
System.Console.WriteLine((fst funAndArgTuple2)(snd funAndArgTuple2))

Передать значение в качестве аргументаPass the Value as an Argument

Если значение имеет состояние первого класса в языке, можно передать его в качестве аргумента функции.If a value has first-class status in a language, you can pass it as an argument to a function. Например, в качестве аргументов часто передаются целые числа и строки.For example, it is common to pass integers and strings as arguments. В следующем коде показаны целые числа и строки, передаваемые в качестве аргументов в F #.The following code shows integers and strings passed as arguments in F#.

// An integer is passed to squareIt. Both squareIt and num are defined in
// previous examples.
//let num = 10
//let squareIt = fun n -> n * n
System.Console.WriteLine(squareIt num)

// String.
// Function repeatString concatenates a string with itself.
let repeatString = fun s -> s + s

// A string is passed to repeatString. HelloHello is returned and displayed.
let greeting = "Hello"
System.Console.WriteLine(repeatString greeting)

Если функции имеют состояние первого класса, необходимо иметь возможность передавать их как аргументы аналогичным образом.If functions have first-class status, you must be able to pass them as arguments in the same way. Помните, что это первая характеристика функций высшего порядка.Remember that this is the first characteristic of higher-order functions.

В следующем примере функция applyIt имеет два параметра: op и arg .In the following example, function applyIt has two parameters, op and arg. Если вы отправляете в функцию, имеющую один параметр для op , и соответствующий аргумент функции arg , функция возвращает результат применения функции op к arg .If you send in a function that has one parameter for op and an appropriate argument for the function to arg, the function returns the result of applying op to arg. В следующем примере аргументы функции и целочисленный аргумент отправляются одинаковым образом с использованием их имен.In the following example, both the function argument and the integer argument are sent in the same way, by using their names.

// Define the function, again using lambda expression syntax.
let applyIt = fun op arg -> op arg

// Send squareIt for the function, op, and num for the argument you want to
// apply squareIt to, arg. Both squareIt and num are defined in previous
// examples. The result returned and displayed is 100.
System.Console.WriteLine(applyIt squareIt num)

// The following expression shows the concise syntax for the previous function
// definition.
let applyIt2 op arg = op arg
// The following line also displays 100.
System.Console.WriteLine(applyIt2 squareIt num)

Возможность отправки функции в качестве аргумента в другую функцию зависит от общих абстракций в языках функционального программирования, таких как операции Map или Filter.The ability to send a function as an argument to another function underlies common abstractions in functional programming languages, such as map or filter operations. Операция Map, например, — это функция более высокого порядка, которая захватывает вычисления, совместно используемые функциями, которые пошагово проходят по списку, выполняют какие-либо действия с каждым элементом, а затем возвращают список результатов.A map operation, for example, is a higher-order function that captures the computation shared by functions that step through a list, do something to each element, and then return a list of the results. Может потребоваться увеличить каждый элемент в списке целых чисел или квадратный каждый элемент или изменить каждый элемент в списке строк на верхний.You might want to increment each element in a list of integers, or to square each element, or to change each element in a list of strings to uppercase. Подверженная ошибкам часть вычислений является рекурсивным процессом, который проходит по списку и создает список возвращаемых результатов.The error-prone part of the computation is the recursive process that steps through the list and builds a list of the results to return. Эта часть захватывается функцией сопоставления.That part is captured in the mapping function. Все, что необходимо написать для конкретного приложения, — это функция, которую необходимо применить к каждому элементу списка по отдельности (Добавление, возведение в изменяемый регистр).All you have to write for a particular application is the function that you want to apply to each list element individually (adding, squaring, changing case). Эта функция отправляется в качестве аргумента функции сопоставления, так же как и squareIt applyIt в предыдущем примере.That function is sent as an argument to the mapping function, just as squareIt is sent to applyIt in the previous example.

F # предоставляет методы Map для большинства типов коллекций, включая списки, массивыи последовательности.F# provides map methods for most collection types, including lists, arrays, and sequences. В следующих примерах используются списки.The following examples use lists. Синтаксис: List.map <the function> <the list>.The syntax is List.map <the function> <the list>.

// List integerList was defined previously:
//let integerList = [ 1; 2; 3; 4; 5; 6; 7 ]

// You can send the function argument by name, if an appropriate function
// is available. The following expression uses squareIt.
let squareAll = List.map squareIt integerList

// The following line displays [1; 4; 9; 16; 25; 36; 49]
printfn "%A" squareAll

// Or you can define the action to apply to each list element inline.
// For example, no function that tests for even integers has been defined,
// so the following expression defines the appropriate function inline.
// The function returns true if n is even; otherwise it returns false.
let evenOrNot = List.map (fun n -> n % 2 = 0) integerList

// The following line displays [false; true; false; true; false; true; false]
printfn "%A" evenOrNot

Дополнительные сведения см. в разделе списки.For more information, see Lists.

Возврат значения из вызова функцииReturn the Value from a Function Call

Наконец, если функция имеет состояние первого класса в языке, необходимо иметь возможность возвращать его как значение вызова функции, как и другие типы, такие как целые числа и строки.Finally, if a function has first-class status in a language, you must be able to return it as the value of a function call, just as you return other types, such as integers and strings.

Следующие вызовы функции возвращают целые числа и отображают их.The following function calls return integers and display them.

// Function doubleIt is defined in a previous example.
//let doubleIt = fun n -> 2 * n
System.Console.WriteLine(doubleIt 3)
System.Console.WriteLine(squareIt 4)

Следующий вызов функции возвращает строку.The following function call returns a string.

// str is defined in a previous section.
//let str = "F#"
let lowercase = str.ToLower()

Следующий вызов функции, объявленный встроенным, возвращает логическое значение.The following function call, declared inline, returns a Boolean value. Отображаемое значение — True .The value displayed is True.

System.Console.WriteLine((fun n -> n % 2 = 1) 15)

Возможность возвращать функцию в качестве значения вызова функции — это вторая характеристика функций более высокого порядка.The ability to return a function as the value of a function call is the second characteristic of higher-order functions. В следующем примере checkFor определяется как функция, принимающая один аргумент, item и возвращающая в качестве значения новую функцию.In the following example, checkFor is defined to be a function that takes one argument, item, and returns a new function as its value. Возвращаемая функция принимает список в качестве аргумента, lst и выполняет поиск item в lst .The returned function takes a list as its argument, lst, and searches for item in lst. Если имеется item , функция возвращает true .If item is present, the function returns true. Если item параметр не указан, функция возвращает значение false .If item is not present, the function returns false. Как и в предыдущем разделе, следующий код использует предоставленную функцию списка List. Existsдля поиска в списке.As in the previous section, the following code uses a provided list function, List.exists, to search the list.

let checkFor item =
    let functionToReturn = fun lst ->
                           List.exists (fun a -> a = item) lst
    functionToReturn

Следующий код использует checkFor для создания новой функции, которая принимает один аргумент, список и выполняет поиск 7 в списке.The following code uses checkFor to create a new function that takes one argument, a list, and searches for 7 in the list.

// integerList and stringList were defined earlier.
//let integerList = [ 1; 2; 3; 4; 5; 6; 7 ]
//let stringList = [ "one"; "two"; "three" ]

// The returned function is given the name checkFor7.
let checkFor7 = checkFor 7

// The result displayed when checkFor7 is applied to integerList is True.
System.Console.WriteLine(checkFor7 integerList)

// The following code repeats the process for "seven" in stringList.
let checkForSeven = checkFor "seven"

// The result displayed is False.
System.Console.WriteLine(checkForSeven stringList)

В следующем примере используется состояние первого класса функций в F # для объявления функции, compose которая возвращает композицию двух аргументов функции.The following example uses the first-class status of functions in F# to declare a function, compose, that returns a composition of two function arguments.

// Function compose takes two arguments. Each argument is a function
// that takes one argument of the same type. The following declaration
// uses lambda expresson syntax.
let compose =
    fun op1 op2 ->
        fun n ->
            op1 (op2 n)

// To clarify what you are returning, use a nested let expression:
let compose2 =
    fun op1 op2 ->
        // Use a let expression to build the function that will be returned.
        let funToReturn = fun n ->
                            op1 (op2 n)
        // Then just return it.
        funToReturn

// Or, integrating the more concise syntax:
let compose3 op1 op2 =
    let funToReturn = fun n ->
                        op1 (op2 n)
    funToReturn

Примечание

Более короткая версия см. в следующем разделе "каррированных функции".For an even shorter version, see the following section, "Curried Functions."

Следующий код отправляет две функции в качестве аргументов в compose , оба из которых принимают один аргумент того же типа.The following code sends two functions as arguments to compose, both of which take a single argument of the same type. Возвращаемое значение — это новая функция, которая представляет собой композицию двух аргументов функции.The return value is a new function that is a composition of the two function arguments.

// Functions squareIt and doubleIt were defined in a previous example.
let doubleAndSquare = compose squareIt doubleIt
// The following expression doubles 3, squares 6, and returns and
// displays 36.
System.Console.WriteLine(doubleAndSquare 3)

let squareAndDouble = compose doubleIt squareIt
// The following expression squares 3, doubles 9, returns 18, and
// then displays 18.
System.Console.WriteLine(squareAndDouble 3)

Примечание

F # предоставляет два оператора, << и >> , которые образуют функции.F# provides two operators, << and >>, that compose functions. Например, let squareAndDouble2 = doubleIt << squareIt эквивалентен let squareAndDouble = compose doubleIt squareIt в предыдущем примере.For example, let squareAndDouble2 = doubleIt << squareIt is equivalent to let squareAndDouble = compose doubleIt squareIt in the previous example.

Следующий пример возврата функции в качестве значения вызова функции создает простую игру подбора.The following example of returning a function as the value of a function call creates a simple guessing game. Чтобы создать игру, вызовите метод makeGame с тем значением, в котором вы хотите угадать target .To create a game, call makeGame with the value that you want someone to guess sent in for target. Возвращаемое значение функции makeGame — это функция, которая принимает один аргумент (Guess) и сообщает, верно ли предположение.The return value from function makeGame is a function that takes one argument (the guess) and reports whether the guess is correct.

let makeGame target =
    // Build a lambda expression that is the function that plays the game.
    let game = fun guess ->
                   if guess = target then
                      System.Console.WriteLine("You win!")
                   else
                      System.Console.WriteLine("Wrong. Try again.")
    // Now just return it.
    game

Следующий код вызывает метод makeGame , отправляя значение 7 для target .The following code calls makeGame, sending the value 7 for target. Идентификатор playGame привязан к возвращенному лямбда-выражению.Identifier playGame is bound to the returned lambda expression. Таким образом, playGame представляет собой функцию, принимающую в качестве одного аргумента значение для guess .Therefore, playGame is a function that takes as its one argument a value for guess.

let playGame = makeGame 7
// Send in some guesses.
playGame 2
playGame 9
playGame 7

// Output:
// Wrong. Try again.
// Wrong. Try again.
// You win!

// The following game specifies a character instead of an integer for target.
let alphaGame = makeGame 'q'
alphaGame 'c'
alphaGame 'r'
alphaGame 'j'
alphaGame 'q'

// Output:
// Wrong. Try again.
// Wrong. Try again.
// Wrong. Try again.
// You win!

Каррированных функцииCurried Functions

Многие примеры в предыдущем разделе можно написать более кратко, используя преимущества неявного карринг в объявлениях функций F #.Many of the examples in the previous section can be written more concisely by taking advantage of the implicit currying in F# function declarations. Карринг — это процесс, преобразуя функцию, имеющую несколько параметров, в ряд встроенных функций, каждый из которых имеет один параметр.Currying is a process that transforms a function that has more than one parameter into a series of embedded functions, each of which has a single parameter. В F # функции, имеющие более одного параметра, по сути являются каррированных.In F#, functions that have more than one parameter are inherently curried. Например, compose из предыдущего раздела можно написать, как показано в следующем кратком стиле с тремя параметрами.For example, compose from the previous section can be written as shown in the following concise style, with three parameters.

let compose4 op1 op2 n = op1 (op2 n)

Однако результатом является функция одного параметра, возвращающая функцию одного параметра, которая, в свою очередь, возвращает другую функцию одного параметра, как показано в compose4curried .However, the result is a function of one parameter that returns a function of one parameter that in turn returns another function of one parameter, as shown in compose4curried.

let compose4curried =
    fun op1 ->
        fun op2 ->
            fun n -> op1 (op2 n)

Доступ к этой функции можно получить несколькими способами.You can access this function in several ways. Каждый из следующих примеров возвращает и отображает 18.Each of the following examples returns and displays 18. compose4 compose4curried В любом из примеров можно заменить на.You can replace compose4 with compose4curried in any of the examples.

// Access one layer at a time.
System.Console.WriteLine(((compose4 doubleIt) squareIt) 3)

// Access as in the original compose examples, sending arguments for
// op1 and op2, then applying the resulting function to a value.
System.Console.WriteLine((compose4 doubleIt squareIt) 3)

// Access by sending all three arguments at the same time.
System.Console.WriteLine(compose4 doubleIt squareIt 3)

Чтобы убедиться, что функция все еще работает, как и раньше, попробуйте повторить исходные тестовые случаи.To verify that the function still works as it did before, try the original test cases again.

let doubleAndSquare4 = compose4 squareIt doubleIt
// The following expression returns and displays 36.
System.Console.WriteLine(doubleAndSquare4 3)

let squareAndDouble4 = compose4 doubleIt squareIt
// The following expression returns and displays 18.
System.Console.WriteLine(squareAndDouble4 3)

Примечание

Можно ограничить карринг, заключив параметры в кортежи.You can restrict currying by enclosing parameters in tuples. Дополнительные сведения см. в разделе "Шаблоны параметров" раздела Параметры и аргументы.For more information, see "Parameter Patterns" in Parameters and Arguments.

В следующем примере используется неявное карринг для записи более короткой версии makeGame .The following example uses implicit currying to write a shorter version of makeGame. Сведения о том, как makeGame конструкции и возвращают game функцию, менее явны в этом формате, но можно проверить с помощью исходных тестовых случаев, в которых результат совпадает.The details of how makeGame constructs and returns the game function are less explicit in this format, but you can verify by using the original test cases that the result is the same.

let makeGame2 target guess =
    if guess = target then
       System.Console.WriteLine("You win!")
    else
       System.Console.WriteLine("Wrong. Try again.")

let playGame2 = makeGame2 7
playGame2 2
playGame2 9
playGame2 7

let alphaGame2 = makeGame2 'q'
alphaGame2 'c'
alphaGame2 'r'
alphaGame2 'j'
alphaGame2 'q'

Дополнительные сведения о карринг см. в разделе "частичное применение аргументов" в функциях.For more information about currying, see "Partial Application of Arguments" in Functions.

Идентификатор и определение функции взаимозаменяемыIdentifier and Function Definition Are Interchangeable

Имя переменной num в предыдущих примерах вычисляется как целое число 10, и это не удивительно, что num допустимо, 10 также является допустимым.The variable name num in the previous examples evaluates to the integer 10, and it is no surprise that where num is valid, 10 is also valid. То же самое касается идентификаторов функций и их значений: где можно использовать имя функции, можно использовать лямбда-выражение, к которому она привязана.The same is true of function identifiers and their values: anywhere the name of the function can be used, the lambda expression to which it is bound can be used.

В следующем примере определяется Boolean функция с именем isNegative , а затем используется имя функции и определение взаимозаменяемой функции.The following example defines a Boolean function called isNegative, and then uses the name of the function and the definition of the function interchangeably. В следующих трех примерах возвращаются и отображаются False .The next three examples all return and display False.

let isNegative = fun n -> n < 0

// This example uses the names of the function argument and the integer
// argument. Identifier num is defined in a previous example.
//let num = 10
System.Console.WriteLine(applyIt isNegative num)

// This example substitutes the value that num is bound to for num, and the
// value that isNegative is bound to for isNegative.
System.Console.WriteLine(applyIt (fun n -> n < 0) 10)

Чтобы сделать это на один шаг дальше, замените значение, applyIt привязанное к для applyIt .To take it one step further, substitute the value that applyIt is bound to for applyIt.

System.Console.WriteLine((fun op arg -> op arg) (fun n -> n < 0)  10)

Функции являются значениями первого класса в F#Functions Are First-Class Values in F#

В примерах, приведенных в предыдущих разделах, показано, что функции в F # удовлетворяют критериям для значений первого класса в F #:The examples in the previous sections demonstrate that functions in F# satisfy the criteria for being first-class values in F#:

  • Идентификатор можно привязать к определению функции.You can bind an identifier to a function definition.
let squareIt = fun n -> n * n
  • Функцию можно сохранить в структуре данных.You can store a function in a data structure.
let funTuple2 = ( BMICalculator, fun n -> n * n )
  • Функцию можно передать в качестве аргумента.You can pass a function as an argument.
let increments = List.map (fun n -> n + 1) [ 1; 2; 3; 4; 5; 6; 7 ]
  • Функцию можно вернуть в качестве значения вызова функции.You can return a function as the value of a function call.
let checkFor item =
    let functionToReturn = fun lst ->
                           List.exists (fun a -> a = item) lst
    functionToReturn

Дополнительные сведения о F # см. в справочнике по языку f #.For more information about F#, see the F# Language Reference.

ПримерExample

ОписаниеDescription

Следующий код содержит все примеры, приведенные в этом разделе.The following code contains all the examples in this topic.

КодCode

// ** GIVE THE VALUE A NAME **


// Integer and string.
let num = 10
let str = "F#"



let squareIt = fun n -> n * n



let squareIt2 n = n * n



// ** STORE THE VALUE IN A DATA STRUCTURE **


// Lists.

// Storing integers and strings.
let integerList = [ 1; 2; 3; 4; 5; 6; 7 ]
let stringList = [ "one"; "two"; "three" ]

// You cannot mix types in a list. The following declaration causes a
// type-mismatch compiler error.
//let failedList = [ 5; "six" ]

// In F#, functions can be stored in a list, as long as the functions
// have the same signature.

// Function doubleIt has the same signature as squareIt, declared previously.
//let squareIt = fun n -> n * n
let doubleIt = fun n -> 2 * n

// Functions squareIt and doubleIt can be stored together in a list.
let funList = [ squareIt; doubleIt ]

// Function squareIt cannot be stored in a list together with a function
// that has a different signature, such as the following body mass
// index (BMI) calculator.
let BMICalculator = fun ht wt ->
                    (float wt / float (squareIt ht)) * 703.0

// The following expression causes a type-mismatch compiler error.
//let failedFunList = [ squareIt; BMICalculator ]


// Tuples.

// Integers and strings.
let integerTuple = ( 1, -7 )
let stringTuple = ( "one", "two", "three" )

// A tuple does not require its elements to be of the same type.
let mixedTuple = ( 1, "two", 3.3 )

// Similarly, function elements in tuples can have different signatures.
let funTuple = ( squareIt, BMICalculator )

// Functions can be mixed with integers, strings, and other types in
// a tuple. Identifier num was declared previously.
//let num = 10
let moreMixedTuple = ( num, "two", 3.3, squareIt )



// You can pull a function out of a tuple and apply it. Both squareIt and num
// were defined previously.
let funAndArgTuple = (squareIt, num)

// The following expression applies squareIt to num, returns 100, and
// then displays 100.
System.Console.WriteLine((fst funAndArgTuple)(snd funAndArgTuple))



// Make a list of values instead of identifiers.
let funAndArgTuple2 = ((fun n -> n * n), 10)

// The following expression applies a squaring function to 10, returns
// 100, and then displays 100.
System.Console.WriteLine((fst funAndArgTuple2)(snd funAndArgTuple2))



// ** PASS THE VALUE AS AN ARGUMENT **


// An integer is passed to squareIt. Both squareIt and num are defined in
// previous examples.
//let num = 10
//let squareIt = fun n -> n * n
System.Console.WriteLine(squareIt num)

// String.
// Function repeatString concatenates a string with itself.
let repeatString = fun s -> s + s

// A string is passed to repeatString. HelloHello is returned and displayed.
let greeting = "Hello"
System.Console.WriteLine(repeatString greeting)



// Define the function, again using lambda expression syntax.
let applyIt = fun op arg -> op arg

// Send squareIt for the function, op, and num for the argument you want to
// apply squareIt to, arg. Both squareIt and num are defined in previous
// examples. The result returned and displayed is 100.
System.Console.WriteLine(applyIt squareIt num)

// The following expression shows the concise syntax for the previous function
// definition.
let applyIt2 op arg = op arg
// The following line also displays 100.
System.Console.WriteLine(applyIt2 squareIt num)



// List integerList was defined previously:
//let integerList = [ 1; 2; 3; 4; 5; 6; 7 ]

// You can send the function argument by name, if an appropriate function
// is available. The following expression uses squareIt.
let squareAll = List.map squareIt integerList

// The following line displays [1; 4; 9; 16; 25; 36; 49]
printfn "%A" squareAll

// Or you can define the action to apply to each list element inline.
// For example, no function that tests for even integers has been defined,
// so the following expression defines the appropriate function inline.
// The function returns true if n is even; otherwise it returns false.
let evenOrNot = List.map (fun n -> n % 2 = 0) integerList

// The following line displays [false; true; false; true; false; true; false]
printfn "%A" evenOrNot



// ** RETURN THE VALUE FROM A FUNCTION CALL **


// Function doubleIt is defined in a previous example.
//let doubleIt = fun n -> 2 * n
System.Console.WriteLine(doubleIt 3)
System.Console.WriteLine(squareIt 4)


// The following function call returns a string:

// str is defined in a previous section.
//let str = "F#"
let lowercase = str.ToLower()



System.Console.WriteLine((fun n -> n % 2 = 1) 15)



let checkFor item =
    let functionToReturn = fun lst ->
                           List.exists (fun a -> a = item) lst
    functionToReturn



// integerList and stringList were defined earlier.
//let integerList = [ 1; 2; 3; 4; 5; 6; 7 ]
//let stringList = [ "one"; "two"; "three" ]

// The returned function is given the name checkFor7.
let checkFor7 = checkFor 7

// The result displayed when checkFor7 is applied to integerList is True.
System.Console.WriteLine(checkFor7 integerList)

// The following code repeats the process for "seven" in stringList.
let checkForSeven = checkFor "seven"

// The result displayed is False.
System.Console.WriteLine(checkForSeven stringList)



// Function compose takes two arguments. Each argument is a function
// that takes one argument of the same type. The following declaration
// uses lambda expresson syntax.
let compose =
    fun op1 op2 ->
        fun n ->
            op1 (op2 n)

// To clarify what you are returning, use a nested let expression:
let compose2 =
    fun op1 op2 ->
        // Use a let expression to build the function that will be returned.
        let funToReturn = fun n ->
                            op1 (op2 n)
        // Then just return it.
        funToReturn

// Or, integrating the more concise syntax:
let compose3 op1 op2 =
    let funToReturn = fun n ->
                        op1 (op2 n)
    funToReturn



// Functions squareIt and doubleIt were defined in a previous example.
let doubleAndSquare = compose squareIt doubleIt
// The following expression doubles 3, squares 6, and returns and
// displays 36.
System.Console.WriteLine(doubleAndSquare 3)

let squareAndDouble = compose doubleIt squareIt
// The following expression squares 3, doubles 9, returns 18, and
// then displays 18.
System.Console.WriteLine(squareAndDouble 3)



let makeGame target =
    // Build a lambda expression that is the function that plays the game.
    let game = fun guess ->
                   if guess = target then
                      System.Console.WriteLine("You win!")
                   else
                      System.Console.WriteLine("Wrong. Try again.")
    // Now just return it.
    game



let playGame = makeGame 7
// Send in some guesses.
playGame 2
playGame 9
playGame 7

// Output:
// Wrong. Try again.
// Wrong. Try again.
// You win!

// The following game specifies a character instead of an integer for target.
let alphaGame = makeGame 'q'
alphaGame 'c'
alphaGame 'r'
alphaGame 'j'
alphaGame 'q'

// Output:
// Wrong. Try again.
// Wrong. Try again.
// Wrong. Try again.
// You win!



// ** CURRIED FUNCTIONS **


let compose4 op1 op2 n = op1 (op2 n)



let compose4curried =
    fun op1 ->
        fun op2 ->
            fun n -> op1 (op2 n)



// Access one layer at a time.
System.Console.WriteLine(((compose4 doubleIt) squareIt) 3)

// Access as in the original compose examples, sending arguments for
// op1 and op2, then applying the resulting function to a value.
System.Console.WriteLine((compose4 doubleIt squareIt) 3)

// Access by sending all three arguments at the same time.
System.Console.WriteLine(compose4 doubleIt squareIt 3)



let doubleAndSquare4 = compose4 squareIt doubleIt
// The following expression returns and displays 36.
System.Console.WriteLine(doubleAndSquare4 3)

let squareAndDouble4 = compose4 doubleIt squareIt
// The following expression returns and displays 18.
System.Console.WriteLine(squareAndDouble4 3)



let makeGame2 target guess =
    if guess = target then
       System.Console.WriteLine("You win!")
    else
       System.Console.WriteLine("Wrong. Try again.")

let playGame2 = makeGame2 7
playGame2 2
playGame2 9
playGame2 7

let alphaGame2 = makeGame2 'q'
alphaGame2 'c'
alphaGame2 'r'
alphaGame2 'j'
alphaGame2 'q'



// ** IDENTIFIER AND FUNCTION DEFINITION ARE INTERCHANGEABLE **


let isNegative = fun n -> n < 0

// This example uses the names of the function argument and the integer
// argument. Identifier num is defined in a previous example.
//let num = 10
System.Console.WriteLine(applyIt isNegative num)

// This example substitutes the value that num is bound to for num, and the
// value that isNegative is bound to for isNegative.
System.Console.WriteLine(applyIt (fun n -> n < 0) 10)



System.Console.WriteLine((fun op arg -> op arg) (fun n -> n < 0)  10)



// ** FUNCTIONS ARE FIRST-CLASS VALUES IN F# **

//let squareIt = fun n -> n * n


let funTuple2 = ( BMICalculator, fun n -> n * n )



let increments = List.map (fun n -> n + 1) [ 1; 2; 3; 4; 5; 6; 7 ]


//let checkFor item =
//    let functionToReturn = fun lst ->
//                           List.exists (fun a -> a = item) lst
//    functionToReturn

См. такжеSee also