#.net #f# #functional-programming
#.net #f# #функциональное программирование
Вопрос:
Я хочу сделать так, чтобы возвращалось среднее значение списка с плавающей запятой (в виде числа с плавающей запятой), если список, переданный в качестве аргумента, представляет собой список чисел с плавающей запятой, а не что-либо еще, например, список целых чисел. Он должен вычислять среднее значение только в том случае, если это список с плавающей точкой.
Допустим, я передаю список целых чисел, тогда он должен вернуть оператор none. Или, если я передаю пустой список, он также должен возвращать оператор none .
let avg x =
let average = List.averageBy (fun elem -> float elem) x
match List.tryFind x with
|Some(x) -> printfn "The average of the list is %f" average
|None -> "There are no float elements in the list, hence no average"
avg [1.0;5.0;6.0;10.0]
получение ошибки FS0001: ожидалось, что это выражение будет иметь тип «a-> bool», но здесь имеет тип «b list».
Что имеет смысл. Однако я не могу понять, где присвоить ему значение bool.
Редактировать
Я рассматриваю возможность использования List.forall, чтобы проверить, содержит ли список числа с плавающей запятой. Все еще сталкиваюсь с той же проблемой BOOL.
Комментарии:
1. Зачем вам нужно проверять, является ли список списком с плавающей запятой? Система типов сделает это за вас. Просто сделайте
List.average [your list]
.2. Это задание, которое у меня есть. Поэтому я должен использовать определенные функции из библиотеки списков. Я могу легко просто вычислить среднее значение по списку, используя мой первый оператор, но я хочу использовать List.tryFind и параметр, поэтому он вернет среднее значение, если есть список, а моя строка — нет.
3. Что вы подразумеваете под «если такой список существует»? Если какие списки существуют? Единственный список здесь — это список с плавающей запятой, который вы получаете в качестве аргумента, и который существует, потому что иначе вы не смогли бы получить его в качестве аргумента. Вы, кажется, пытаетесь использовать
tryFind
, чтобы проверить, является ли список пустым (что не то же самое, что не существует), но это не то, для чего нужен tryFind, вы бы использовалиList.isEmpty
для этого.4. Знаете ли вы, что такое строгая типизация?
5. Каков тип аргумента? Это an
obj list
obj
или что-то еще?
Ответ №1:
Ярлык для ответа
Следующее предполагает, что вам передается список различных типов, который, как я предполагаю, так и есть, поскольку вы сказали, что должны использовать функции из List
модуля.
let average list =
let isFloat (x : obj) =
match x with
| :? float -> true
| _ -> false
let toFloat (x : obj) = x :?> float
if List.forall isFloat list then
List.averageBy toFloat list
|> Some
else None
Объяснение
Причина, по которой вы получаете ошибку компилятора, заключается List.tryFind
в неправильном использовании, но это еще не вся история. Правильное использование tryFind
выглядит следующим образом
let findFirstEven list = List.tryFind (fun x -> x % 2 = 0) list
F # строго типизирован, что означает, что в общем случае мы ожидаем, что вы не будете пытаться передавать функции нескольких типов. Функции ожидают ровно один тип для каждого аргумента, и передача чего-то другого является ошибкой времени компиляции.
Тем не менее, поскольку F # построен на .NET и поддерживает классы и наследование, функция может принимать класс, но для него выполнять разные действия, если фактический переданный объект является производным от ожидаемого класса. В F # этот тест на приведение к типу выполняется следующим образом:
type Person (name) =
member this.Name : string = name
type Student (name, university) =
inherit Person (name)
member this.University : string = university
type Adult (name, job) =
inherit Person (name)
member this.Job : string = job
let greet (person : Person) =
match person with
| :? Student as student -> printfn "Hello, %s of %s" student.Name student.University
| :? Adult as adult -> printfn "Hello, %s. I hope you enjoy working at %s!" adult.Name adult.Job
| _ -> printfn "Nice to meet you, %s" person.Name
Это работает только для производных типов, а не для истинных универсальных типов F #, таких как попытка сопоставления 'T list
с float list
или что-то еще. Способ .Однако структурированность NET означает, что каждый возможный тип является либо obj
, либо производным от obj
. Это означает, что если нам разрешено передавать любой возможный тип, мы можем проверить, является ли это a float list
или нет.
let average (value : obj) =
match value with
| :? List<float> as list -> List.average list |> Some
| _ -> None
Эта функция может принимать буквально любой выражаемый тип, поэтому мы действительно стараемся избегать написания в F #. Предположим, вместо этого вы можете гарантировать, что вам будет передан список вещей, но вещи могут быть разных типов друг от друга. Опять же, это ситуация, которую мы определенно стараемся избегать при написании F #, но пока:
let average list =
let isFloat (x : obj) =
match x with
| :? float -> true
| _ -> false
let toFloat (x : obj) = x :?> float
if List.forall isFloat list then
List.averageBy toFloat list
|> Some
else None
Это индивидуально проверяет, является ли каждый элемент списка a float
, затем, если это так, приводит элементы списка к float
и усредняет их. Понижение :?>
необходимо, потому что в противном List.average
случае он не знает, как выполнить среднее значение (он по-прежнему считает, что список может быть любым), но это также опасный оператор для небрежного использования. Если приведение к производному типу завершается неудачно, то код выдаст исключение, которое может завершить работу вашей программы. Здесь мы знаем, что это безопасно, потому что мы только что протестировали то же приведение, но вам следует избегать его использования в своем коде, если вы не уверены, что это безопасно. Даже тогда это не приводит к идиоматическому коду F #, потому что мы в основном ожидаем, что вы будете использовать настоящие дженерики для правильного использования системы типов.
Комментарии:
1. Спасибо за отличный и подробный ответ. Определенно имеет смысл. Все еще пытаюсь понять этот язык. Очень признателен.
Ответ №2:
Вот что должно сработать :
let avg (arg : obj) =
match arg with
:? list<float> as xs -> Some (List.average xs)
| _ -> None
или (то же самое, другой способ его записи) :
let avg : obj -> _ = function
:? list<float> as xs -> Some (List.average xs)
| _ -> None