F # странная проблема printfn - PullRequest
7 голосов
/ 24 июня 2009

Я играл с F # (Visual Studio 2010 beta 1) и написал небольшой консольный скрипт, который попросил пользователя ввести 2 числа и оператор, а затем выполнил его. Он отлично работает, кроме крошечной, но раздражающей вещи: иногда мои инструкции printfn игнорируются. Я установил точки останова в коде, чтобы увидеть, что это действительно так.

Фрагмент кода:

let convert (source : string) =
    try System.Int32.Parse(source)
    with :? System.FormatException ->
        printfn "'%s' is not a number!" source;
        waitForExitKey();
        exit 1

let read =
    printfn "Please enter a number.";
    System.Console.ReadLine

let num1 : int = read() |> convert // the printfn in the read function is run...
let num2 : int = read() |> convert // ... but here is ignored

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

Так что мой вопрос довольно прост: что вызывает эту проблему с printfn? Я что-то не так делаю?

Спасибо заранее, ShdNx

Ответы [ 2 ]

16 голосов
/ 24 июня 2009

Эта страница содержит частичное объяснение того, что происходит, но короткая и приятная версия заключается в том, что F # выполнит любое значение в объявлении, если оно не принимает параметры.

let read =
    printfn "Please enter a number."
    System.Console.ReadLine

Поскольку read не принимает никаких параметров, он выполняется сразу после объявления и связывает возвращаемое значение функции с идентификатором read.

Кстати, ваше возвращаемое значение является функцией типа (unit -> string). Это происходит потому, что F # автоматически карри функционирует , если им не передаются все их параметры. ReadLine ожидает один параметр модуля, но поскольку он не передается, вы фактически привязываете read к самой функции ReadLine.

Решение заключается в следующем:

let read() = // read takes one unit parameter
    printfn "Please enter a number."
    System.Console.ReadLine() // pass paramter to ReadLine method

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

8 голосов
/ 24 июня 2009

Я понимаю, что это может сбить с толку. В вашем примере printfn запускается раньше, чем вы думаете. Фактически он будет выполняться даже без вызова read(), т.е. закомментируйте последние две строки, и вы все равно увидите, что сообщение печатается.

Я думаю, что ваше намерение примерно такое:

let read() =
    printfn "Please enter a number.";
    System.Console.ReadLine()

Это создаст «повторно используемую» функцию вместо привязки функции к идентификатору, как в исходном примере.

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

let read() =
    printfn "Please enter a number."
    System.Console.ReadLine()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...