F#: Почему я должен явно указать 'единицу' для функций, берущих аргументы?

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

После испытания некоторого пугающегося поведения я пришел к пониманию, что F# делает дифференцирование между:

prepareDeck = allSuits |> List.collect generateCards |> shuffle

и

prepareDeck() = allSuits |> List.collect generateCards |> shuffle

Я заметил, что это "кэширует" первого, никогда не повторно вычисляя его, если это называют снова, тогда как это рассматривает последнего как нормальная функция. Вы не можете сказать различие, если рассматриваемая функция не имеет побочных эффектов, очевидно, но моего shuffle сделал!

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

8
задан J Cooper 30 January 2010 в 00:38
поделиться

2 ответа

Большая часть материала f # объясняет, что все утверждения верхнего уровня в модуле выполняются из сверху вниз на объявлении. Другими словами, то, что вы объявили, не является функцией, но значение, которое ограничено один раз, когда программа работает.

Это действительно помогает увидеть отраженный код. У меня простой файл:

let juliet = "awesome"
let juliet2() = "awesome"

Компилированный код выглядит что-то подобное:

public static string juliet
{
    [CompilerGenerated, DebuggerNonUserCode]
    get
    {
        return "awesome";
    }
}

//...

public static string juliet2()
{
    return "awesome";
}

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

let x = someLongRunningDatabaseCall()

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

Кроме того, мы можем написать интересный код, как это:

> let isInNebraska =
    printfn "Creating cities set"
    let cities = set ["Omaha"; "Bellevue"; "Lincoln"; "Papillion"; "La Vista"; "Ralston"]
    fun n -> cities.Contains(n);;
Creating cities set

val isInNebraska : (string -> bool)

> isInNebraska "Omaha";;
val it : bool = true

> isInNebraska "Okaloosa";;
val it : bool = false

с ISinnebraska - это значение, его оценивается немедленно. Просто так бывает, что его тип данных (String -> Bool) , так что выглядит , как функция. В результате мы заполняем только наши городов , даже если мы вызовим функцию 1000 раз.

Давайте сравним этот код к этому:

> let isInNebraska2 n =
    printfn "Creating cities set"
    let cities = set ["Omaha"; "Bellevue"; "Lincoln"; "Papillion"; "La Vista"; "Ralston"]
    cities.Contains(n);;

val isInNebraska2 : string -> bool

> isInNebraska2 "Omaha";;
Creating cities set
val it : bool = true

> isInNebraska2 "Okaloosa";;
Creating cities set
val it : bool = false

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

Так что определенно существует законное и реальное различие между значениями и функциями.

15
ответ дан 5 December 2019 в 07:58
поделиться

Как отметили другие, для свойств в любом случае потребуется частное защитное поле.

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

-121--1255490-

Поля - единственное место, где можно хранить состояние. Свойства на самом деле являются всего лишь парой методов со специальным синтаксисом, который позволяет им быть сопоставленными с методом get или set в зависимости от того, как они используются: если свойство изменяет или обращается к состоянию, это состояние все равно должно храниться в поле.

Поля не всегда отображаются. С автоматическими свойствами C # 3 поле создается для вас компилятором. Но он все еще там. Кроме того, автоматические свойства имеют некоторые существенные ограничения (например, нет поддержки INotifyPropertyChanged, нет бизнес-логики в установщиках), которые означают, что они часто неуместны, и в любом случае необходимо создать явное поле и свойство, определенное вручную.

В соответствии с ответом Дэвида , вы правы, если говорите об API: вы почти никогда не хотите сделать внутреннее состояние (поля) частью API.

-121--1255492-

Так все работает практически на каждом языке с побочными эффектами.

let name = expr

запускает код 'now' и может вызвать побочные эффекты, если expr имеет эффекты. Последующие ссылки на name не имеют последствий. Тогда как

let name() = expr

определяет функцию, не имеет эффектов сейчас и будет оценивать (и иметь эффекты) каждый раз при вызове name () .

6
ответ дан 5 December 2019 в 07:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: