F#:предупреждение FS0020:Это выражение должно иметь тип 'unit', но имеет тип 'bool'
Вопрос
Я пытаюсь выучить 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