Рекусивное сканирование через иерархию списка различных типов записей

У меня есть структура данных, которая состоит из списка записей F#, для которого один из членов сам является списком записей другого типа, так что примерно до 4 уровней глубокой иерархии. Код, который я должен создать для этой структуры, немного многословен, но работает. Теперь я хочу создать универсальную хвостовую рекурсивную функцию, которая разлагает списки этой структуры данных с верхнего уровня в иерархии, чтобы создать карту подсчета количества элементов на нижнем уровне списка иерархии. Я могу разработать необходимый код, создав функции для разложения записей на каждом уровне иерархии, но в итоге вы получите одну и ту же рекурсивную функцию для обработки списков, но разных типов записей. Вот как я пытался реализовать это не многословно, но получаю следующую ошибку:

Это приведение во время выполнения или тест типа от типа
'a
в
MarshallingPanel
включает неопределенный тип, основанный на информации до этой точки программы. Типовые тесты во время выполнения не разрешены для некоторых типов.

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

let rec mapAsRequired items (currentCBMap: Map<string*string*string*string, int>) =
    match items with
        | head :: tail ->
            match head with
                | :? MarshallingPanel as marshallingPanel ->
                        mapAsRequired marshallingPanel.PLCs currentCBMap
                | :? PLC as plc ->
                        mapAsRequired plc.Racks currentCBMap
                | :? Rack as rack ->
                        mapAsRequired rack.Slots currentCBMap
                | _ ->
                    mapAsRequired [] currentCBMap
            mapAsRequired tail currentCBMap
        | [] ->
            currentCBMap

let rec mapMarshallingPanels (marshallingPanels:MarshallingPanel list) (currentCBMap: Map<string*string*string*string, int>) = 
    match marshallingPanels with
        | head :: tail ->
            mapMarshallingPanels tail (mapAsRequired (List.sortBy(fun (plc:PLC) -> rankProcessorForCBAlllocation plc.PLCNo) head.PLCs) currentCBMap)
        | [] ->
            currentCBMap

mapAsRequired marshallingPanels Map.empty

1 ответ

Чтобы решить проблему, вам нужно сопоставить что-то типа obj а не на значение неопределенного типа (параметр типа 'a). Вы можете сделать это, добавив box:

match box head with 
| :? MarshallingPanel as marshallingPanel -> 
    mapAsRequired marshallingPanel.PLCs currentCBMap 
| :? PLC as plc -> 
    mapAsRequired plc.Racks currentCBMap 

Однако я полностью согласен с замечанием Джона Палмера о том, что использование дискриминационного союза кажется лучшим выбором для вашей цели.

Другие вопросы по тегам