Что означает -> в F #? - PullRequest
       60

Что означает -> в F #?

25 голосов
/ 19 сентября 2008

Я некоторое время пытался войти и выключить F #, но меня все время откладывают. Почему?

Потому что независимо от того, на какой ресурс «новичков» я пытаюсь взглянуть, я вижу очень простые примеры, которые начинают использовать оператор ->.

Тем не менее, я нигде еще не нашел, который дает ясное простое объяснение того, что означает этот оператор. Как будто это должно быть настолько очевидно, что не нужно объяснения даже для завершения новичков.

Поэтому я должен быть действительно плотным или, может быть, это почти 3 десятилетия предыдущего опыта, удерживающего меня.

Может кто-нибудь объяснить, объяснить или указать на действительно доступный ресурс, который это объясняет?

Ответы [ 9 ]

48 голосов
/ 20 сентября 2008

'->' не является оператором. Он встречается в синтаксисе F # в нескольких местах, и его значение зависит от того, как он используется как часть более крупной конструкции.

Внутри типа '->' описывает типы функций, как люди описали выше. Например

let f : int -> int = ...

говорит, что 'f' - это функция, которая принимает int и возвращает int.

Внутри лямбды («вещь, начинающаяся с ключевого слова fun»), «->» - это синтаксис, отделяющий аргументы от тела. Например

fun x y -> x + y + 1

- это выражение, которое определяет функцию с двумя аргументами для данной реализации.

Внутри конструкции match, '->' - это синтаксис, отделяющий шаблоны от кода, который должен выполняться, если шаблон соответствует. Например, в

match someList with
| [] -> 0
| h::t -> 1

материал слева от каждого '->' - это шаблоны, а материал справа - это то, что происходит, если шаблон слева совпал.

Трудность понимания может быть коренится в ошибочном предположении, что «->» - это «оператор» с одним значением. Аналогия может быть "." в C #, если вы никогда не видели код раньше, и попробуйте проанализировать "." Оператор, основанный на взглядах на «obj.Method» и «3.14» и «System.Collections», может запутаться, потому что символ имеет разные значения в разных контекстах. Однако, как только вы узнаете достаточно языка, чтобы распознать эти контексты, все станет ясно.

12 голосов
/ 19 сентября 2008

Это в основном означает «карты». Прочитайте это так или как «превращается в» или что-то в этом роде.

Итак, из F # за 20 минут учебник,

> List.map (fun x -> x % 2 = 0) [1 .. 10];;
val it : bool list
= [false; true; false; true; false; true; false; true; false; true]

Код (забавный i -> i% 2 = 0) определяет анонимная функция, называемая лямбда выражение, которое имеет параметр х и функция возвращает результат "x % 2 = 0 ", является ли х даже.

9 голосов
/ 20 сентября 2008

Первый вопрос - знакомы ли вы с лямбда-выражениями в C #? Если это так, то -> в F # совпадает с => в C # (я думаю, что вы прочитали, что это «идет»).

Оператор -> также можно найти в контексте сопоставления с шаблоном

match x with
| 1 -> dosomething
| _ -> dosomethingelse

Я не уверен, является ли это также лямбда-выражением или чем-то еще, но я предполагаю, что «идет к» все еще сохраняется.

Возможно, вы действительно имеете в виду «загадочные» ответы синтаксического анализатора F #:

> let add a b = a + b
val add: int -> int -> int

Это означает (как объясняет большинство примеров), что add - это 'val', который принимает два целых числа и возвращает int. Для меня это было совершенно непрозрачно с самого начала. Я имею в виду, откуда мне знать, что add - это не val, который принимает одно целое и возвращает два целых числа?

Ну, дело в том, что в некотором смысле это так. Если я добавлю только один int, я получу обратно (int -> int):

> let inc = add 1
val inc: int -> int

Это (карри) - одна из тех вещей, которые делают F # такой сексуальной для меня.

Для получения полезной информации о F # я обнаружил, что блоги на FAR более полезны, чем любая официальная «документация»: вот некоторые имена, которые нужно проверить

4 голосов
/ 19 сентября 2008

(a -> b) означает «функция от a до b». В аннотации типа он обозначает тип функции. Например, f: (int -> String) означает, что f ссылается на функцию, которая принимает целое число и возвращает строку. Он также используется в качестве consttructor таких значений, как в

val f : (int -> int) = fun n -> n * 2

, который создает значение, которое является функцией от некоторого числа n до того же числа, умноженного на два.

1 голос
/ 21 сентября 2008

Много отличных ответов на эти вопросы, спасибо людям. Я хотел бы поместить здесь редактируемый ответ, который объединяет вещи.

Для тех, кто знаком с пониманием C # -> быть тем же, что и => выражение lamba - хороший первый шаг. Это использование: -

fun x y -> x + y + 1

Может пониматься как эквивалент: -

(x, y) => x + y + 1;

Однако ясно, что -> имеет более фундаментальное значение, которое проистекает из концепции, что функция, которая принимает два параметра, таких как выше, может быть уменьшена (это правильный термин?) До серии функций, принимающих только один параметр.

Следовательно, когда вышеизложенное описывается так: -

Int -> Int -> Int

Это действительно помогло узнать, что -> правильно ассоциативно, поэтому можно рассмотреть вышесказанное: -

Int -> (Int -> Int)

Aha! У нас есть функция, которая принимает Int и возвращает (Int -> Int) (карри функция?).

Также помогло объяснение, что -> также может появляться как часть определения типа. (Int -> Int) - это тип любой функции, которая принимает Int и возвращает Int.

Также полезно -> появляется в другом синтаксисе, таком как сопоставление, но там это не имеет того же значения? Это верно? Я не уверен, что это так. Я подозреваю, что это имеет то же значение, но у меня пока нет слов, чтобы выразить это.

Обратите внимание, что цель этого ответа не в том, чтобы создавать дальнейшие ответы, а в том, чтобы вы, люди, совместно редактировали его, чтобы создать более точный ответ. Крайне было бы хорошо, чтобы все неопределенности и ошибки (такие как этот параграф) были удалены и добавлены лучшие примеры. Давайте постараемся сделать этот ответ как можно более доступным для непосвященных.

1 голос
/ 20 сентября 2008

В контексте определения функции она похожа на => из лямбда-выражения в C # 3.0.

F#: let f = fun x -> x*x
C#: Func<int, int> f = x => x * x;

-> в F # также используется в сопоставлении с образцом, где это означает: если выражение соответствует части между | и ->, то то, что следует после ->, должно быть возвращено как результат:

let isOne x = match x with
 | 1 -> true
 | _ -> false
1 голос
/ 19 сентября 2008

Здесь уже есть много отличных ответов, я просто хочу добавить к разговору еще один способ думать об этом.

'->' означает функцию.

'a ->' b - это функция, которая принимает 'a и возвращает a' b

('a *' b) -> ('c *' d) - это функция, которая принимает кортеж типа ('a,' b) и возвращает кортеж ('c,' d). Например, int / string возвращает float / char.

Где это становится интересным, это каскадный случай 'a ->' b -> 'c. Это функция, которая принимает 'a и возвращает функцию (' b -> 'c), или функцию, которая принимает' b -> 'c.

Итак, если вы напишите: пусть f x y z = ()

Тип будет f: 'a ->' b -> 'c -> unit, поэтому, если вы примените только первый параметр, результатом будет каррированная функция' b -> 'c ->' unit.

1 голос
/ 19 сентября 2008

С Microsoft :

Типы функций - это типы, данные первоклассные значения функций и написано int -> int. Они подобны типам делегатов .NET, кроме не даны имена. Все функции F # идентификаторы могут быть использованы в качестве первого класса значения функций и аноним Значения функций могут быть созданы с помощью (fun ... -> ...) форма выражения.

0 голосов
/ 19 сентября 2008

Хорошая вещь в таких языках, как Haskell (в F # она очень похожа, но я не знаю точного синтаксиса - это должно помочь вам понять ->, хотя) в том, что вы можете применять только части аргумента, для создания curry функций:

adder n x y = n + x + y

Другими словами: «дай мне три вещи, и я сложу их вместе». Когда вы кидаете числа в него, компилятор выводит типы n x и y. Скажи, что пишешь

adder 1 2 3

Тип 1, 2 и 3 - Int. Поэтому:

adder :: Int -> Int -> Int -> Int

То есть, дайте мне три целых числа, и я, в конце концов, стану целым числом или то же самое, что и сказать:

five :: Int
five = 5

Но вот хорошая часть! Попробуйте это:

add5 = adder 5

Как вы помните, adder принимает int, int, int и возвращает вам int. Однако это не вся правда, как вы скоро увидите. Фактически, add5 будет иметь такой тип:

add5 :: Int -> Int -> Int

Это будет, как если бы вы "отцепили" целые числа (крайние слева) и приклеили их непосредственно к функции. Если присмотреться к сигнатуре функции, мы заметим, что -> являются ассоциативными справа, т. Е .:

addder :: Int -> (Int -> (Int -> Int))

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

add5andtwomore :: Int -> (Int -> Int)
add5andtwomore = adder 5

Теперь вы можете использовать add5andtwomore вместо "adder 5". Таким образом, вы можете применить другое целое число, чтобы получить (скажем) «add5and7andonemore»:

add5and7andonemore :: Int -> Int
add5and7andonemore = adder 5 7

Как видите, add5and7andone больше хочет точно другой аргумент, и когда вы его дадите, он внезапно станет целым числом!

  > add5and7andonemore 9
 => ((add5andtwomore) 7) 9
 => ((adder 5) 7) 9)
<=> adder 5 7 9

Подставляя параметры к сумматору (n x y) для (5 7 9), получаем:

  > adder 5 7 9 = 5 + 7 + 9
 => 5 + 7 + 9
 => 21

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

  > 5 + 7 + 9
 => (+ 5 (+ 7 9))
 => (+ 5 16)
 => 21

Вот, пожалуйста!

...