F#:предупреждение FS0020:Это выражение должно иметь тип 'unit', но имеет тип 'bool'

StackOverflow https://stackoverflow.com/questions/1108865

  •  12-09-2019
  •  | 
  •  

Вопрос

Я пытаюсь выучить 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 <- номер +20".Я пробовал переписывать и перемещать выражения, и я всегда получаю эту ошибку в строке под оператором while.

Я пишу это с помощью бета-версии VS2010.

Это было полезно?

Решение

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

f = checkMod nr

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

f <- checkMod nr

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

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

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

Другие советы

Поскольку я могу представить, что эта страница weg становится "каноническим" местом для поиска информации о предупреждение 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    

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

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

let nr =
   let rec compute nr =  
      if checkMod nr then nr else compute (nr + 20)
   compute 0     

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

В приведенном примере:

nr <- nr + 20

оценивает, чтобы unit принимая во внимание

f = checkMod nr

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

#nowarn "0020"

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

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

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

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

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

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

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

В том же духе, что и в сообщении Брайана, вот еще один способ получить предупреждение 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top