F #: предупреждение FS0020: это выражение должно иметь тип 'unit', но имеет тип 'bool' - PullRequest
3 голосов
/ 10 июля 2009

Я пытаюсь выучить F #, пройдя через некоторые проблемы Эйлера, и обнаружил проблему, которую не смог выяснить. Это мое наивное решение.

let compute =
    let mutable f = false
    let mutable nr = 0
    while f = false do
        nr <- nr + 20
        f = checkMod nr
    nr

Когда я делаю это, я получаю предупреждение об ошибке FS0020: это выражение должно иметь тип 'unit', но имеет тип 'bool' в выражении "nr <- nr +20". Я пытался переписать и переместить выражения вокруг, и я всегда получаю эту ошибку в строке ниже оператора while. </p>

Я пишу это, используя VS2010 Beta.

Ответы [ 6 ]

12 голосов
/ 10 июля 2009

Поскольку я могу представить, что эта веб-страница становится «каноническим» местом для поиска информации о предупреждении FS0020 , вот мое краткое изложение трех наиболее распространенных случаев, в которых вы получаете предупреждение, и способы его устранения им.

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

// you are calling a function for its side-effects, intend to ignore result    
let Example1Orig() =
    let sb = new System.Text.StringBuilder()
    sb.Append("hi")       // warning FS0020
    sb.Append(" there")   // warning FS0020
    sb.ToString()

let Example1Fixed() =
    let sb = new System.Text.StringBuilder()
    sb.Append("hi") |> ignore
    sb.Append(" there") |> ignore
    sb.ToString()

Предупреждение полезно, указывая на ошибку (функция не имеет эффекта):

// the warning is telling you useful info 
// (e.g. function does not have an effect, rather returns a value)
let Example2Orig() =
    let l = [1;2;3] 
    List.map (fun x -> x * 2) l    // warning FS0020
    printfn "doubled list is %A" l

let Example2Fixed() =
    let l = [1;2;3] 
    let result = List.map (fun x -> x * 2) l
    printfn "doubled list is %A" result

Запутанный оператор присваивания и оператор сравнения равенства:

// '=' versus '<-'
let Example3Orig() =
    let mutable x = 3
    x = x + 1          // warning FS0020
    printfn "%d" x    

let Example3Fixed() =
    let mutable x = 3
    x <- x + 1
    printfn "%d" x    
10 голосов
/ 10 июля 2009

Следующая строка:

f = checkMod nr

- это проверка на равенство, а не задание, как я полагаю, вы собираетесь. Измените его на:

f <- checkMod nr

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

Кроме того, строка while f = false do действительно должна быть упрощена до while not f do; проверки на равенство логических типов довольно запутанны.

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

1 голос
/ 10 июля 2009

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

Например, вот так:

let nr =
   let rec compute nr =  
      if checkMod nr then nr else compute (nr + 20)
   compute 0     
0 голосов
/ 03 января 2016

В том же духе, что и в публикации Брайана, есть еще один способ получить предупреждение FS0020: Короче говоря, я случайно задал аргументы функции.

Будучи новичком в F #, мне было трудно отладить приведенный ниже код, который для второй строки (пусть gdp ...) выдал предупреждение FS0020: это выражение должно иметь тип 'unit', но имеет тип '(string -> ^ a -> unit) * string * float '. Оказывается, что линия не была проблемой вообще; вместо этого была испорчена строка printfn. Удаление разделителей запятых из списка аргументов исправило это.

for country in wb.Regions.``Arab World``.Countries do
  let gdp = country.Indicators.``GDP per capita (current US$)``.[2010]
  let gdpThous = gdp / 1.0e3
  printfn "%s, %s (%.2f)" country.Name, country.CapitalCity, gdpThous
0 голосов
/ 15 июля 2009

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

В вашем примере вы пытаетесь найти первое кратное 20, которое проходит ваш тест checkMod. Это часть что . Для функциональной части how я рекомендую просмотреть методы, доступные для последовательностей. То, что вам нужно, это первый элемент последовательности (кратный 20), прошедший тест, например:

let multi20 = Seq.initInfinite (fun i -> i*20)
let compute = multi20 |> Seq.find checkMod

Первое предложение генерирует бесконечный список из двадцати (я его составил). Второе let находит первое число в указанном списке, которое проходит ваш тест. Ваша задача - убедиться, что на самом деле есть число, которое пройдет тест, но это, конечно, верно и для императивного кода.

Если вы хотите сжать две вышеупомянутые строки в одну, вы также можете написать

let computeCryptic = Seq.initInfinite ((*) 20) |> Seq.find checkMod

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

0 голосов
/ 10 июля 2009

while выражения в F # потребуется немного привыкнуть, если вы говорите с императивным языком. Каждая строка в выражении while должна иметь значение unit (например, void из C ++ / C #). Общее выражение тогда также оценивается как unit.

В примере:

nr <- nr + 20

оценивается как unit, тогда как

f = checkMod nr

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

#nowarn "0020"
...