#f#
#f#
Вопрос:
Я просто пытаюсь извлечь заголовок из списка, но я получаю сообщение об ошибке, в котором говорится The type 'Government' is not compatible with the type 'seq<government>'.
type Government = {
Id : Id;
Name : string;
Abbreviation : string;
ParentId : string option;
}
type GovernmentStructure<'gov> =
| Root of Government : 'gov * SubGov : GovernmentStructure<'gov> list
| Node of Government : 'gov * SubGov : GovernmentStructure<'gov> list
| Leaf of Government : 'gov
let rec findGovernment (gov : Government) (governmentStructure : GovernmentStructure<Government> list) =
[
for x in governmentStructure do
match x with
| Root(gov', subGov) ->
if gov = gov' then yield gov' else yield! findGovernment gov subGov
| Node(gov', subGov) ->
if gov = gov' then yield gov' else yield! findGovernment gov subGov
| Leaf(gov') ->
if gov = gov' then yield gov'
] |> List.head // This is where I get the error
Ответ №1:
findGovernment
возвращает один элемент (тот, который был возвращен из List.head
), а не коллекцию. Следовательно, вы хотите использовать yield
при его рекурсивном вызове, а не yield!
:
let rec findGovernment (gov : Government)
(governmentStructure : GovernmentStructure<Government> list) =
[
for x in governmentStructure do
match x with
| Root(gov', subGov) ->
if gov = gov' then yield gov' else yield findGovernment gov subGov
| Node(gov', subGov) ->
if gov = gov' then yield gov' else yield findGovernment gov subGov
| Leaf(gov') ->
if gov = gov' then yield gov'
] |> List.head
Комментарии:
1. Да, а также мне пришлось заключить функцию и ее аргументы в круглые скобки как yield (findGovernment gov subGov), чтобы она работала, что изначально меня сбило с толку. Не уверен, почему это так.
2. @user1206480 : Он компилируется чисто, без скобок здесь …
Ответ №2:
Подумайте об этом: какой тип возврата имеет ваша findGovernment
функция?
Если вы посмотрите на инструкцию целиком, то можно было бы ожидать, что это будет Government
— поскольку вы возвращаете результат вызова List.head
чего-то, что предположительно является Government list
.
Однако, если вы заглянете внутрь определения списка, там есть строка yield! findGovernment ...
. Поскольку yield!
инструкция ожидает, что аргумент будет иметь тип seq<_>
, из этого следует, что findGovernment
должен возвращать последовательность чего-либо.
Так что же он возвращает в конце концов? Один Government
или их последовательность? Компилятор не может решить за вас, поэтому он жалуется.
Я думаю, что ваша ошибка заключается в чрезмерной разработке.
Сначала давайте просто подумаем о проблеме: для Root
и Node
случаев результатом является этот узел, если он совпадает, в противном случае результат находится где-то в SubGov
; и для Leaf
случая результатом является этот лист, если он совпадает.
Давайте запишем это:
match x with
| Root(g, _) | Node(g, _) when g = gov -> g
| Root(_,sub) | Node(_, sub) -> findGovernment gov sub
| Leaf g when g = gov -> g
Но подождите, теперь мы сразу видим пробел в этом алгоритме: что, если значения поиска вообще нет в структуре? Что тогда должна возвращать функция? На самом деле есть два варианта: return Government option
, указывающий, что возвращаемого значения может не быть, или просто сбой. Давайте пока для простоты сделаем его аварийным:
match x with
| Root(g, _) | Node(g, _) when g = gov -> g
| Root(_,sub) | Node(_, sub) -> findGovernment gov sub
| Leaf g when g = gov -> g
| _ -> failwith "Can't find it."
Но теперь, посмотрите внимательнее: каждый раз, когда вы возвращаете что-то, вы делаете это только после сравнения этого чего-то с gov
. Другими словами, единственное значение, которое может вернуть эта функция, — это значение, равное gov
. Итак, вопрос: почему бы просто не вернуть gov
?
let findGovernment gov = gov
Я знаю, это кажется глупым, но в этом суть: если вы доводите свою логику до окончательного вывода, и этот вывод оказывается глупым, это означает, что с логикой с самого начала было что-то не так.