Проблема с приведением / преобразованием

#f# #dictionary #casting #match #tuples

#f# #словарь #Кастинг #совпадение #кортежи

Вопрос:

В следующем фрагменте я пытаюсь извлечь recid, содержащийся во вложенном кортеже, который формирует ключ dict. Формат вложенного кортежа (Int32, (логическое значение, boolean)) —

Я ищу значение элемента Int32 (который на самом деле является идентификатором строки для записи базы данных).

В приведенном ниже коде сопоставления я пытаюсь добавить recid в список, но сначала я преобразую объект в целое число.
Однако это приводит к следующей ошибке — не уверен, почему?

Ошибка: Это принудительное выполнение или проверка типа из типа ‘a в int32
включает неопределенный тип, основанный на информации, предшествующей этой программной точке. Тесты типов во время выполнения не разрешены для некоторых типов. Необходимы дополнительные примечания к типу. Словарь, на который здесь делается ссылка, определяется как:

 // Create Dict  
let rdict =  new Dictionary<_,_>()
// Add elements
rdict.Add( (x.["PatientID"],(true,true) ),ldiff) 

// Extract Dict items 
let reclist = new ResizeArray<int32>()
for KeyValue(k,v) in rdict do
match k with
    | ((recid,(true,true)) ->
     printfn "Found a matching Record: %A " recid;   // <- prints correct result
     let n = (recid:?> int32)                            // <- coercion error
         reclist.Add(n)
  

Комментарии:

1. В строке rdict.Add( (x.["PatientID"],(true,true) ) несовпадающее количество открывающих и закрывающих круглых скобок. Каковы Dictionary типы ключа и значения? int*(bool*bool) Предполагается, что это должен быть только тип ключа, или это int тип ключа и bool*bool тип значения? Кроме того, каков фактический тип x.["PatientID"] ? Возвращает ли это int или в штучной упаковке int (т. Е. obj к которому можно привести int )?

2. Упс — я допустил ошибку при публикации строки rdict.add — теперь исправлено. (На самом деле я создаю список пар строк / столбцов в качестве значения dict. Ключ dict на самом деле является составным (rid,(логическое значение, boolean)) значением. (логические значения определяют, существует ли rec в паре таблиц). x.[«PatientID»] — это (распакованное) значение int, полученное из таблицы базы данных.

3. На самом деле, еще один вопрос — собираетесь ли вы копировать идентификаторы из rdict в reclist только в том случае, если их сопровождающая bool пара является true, true , а не какая-либо другая комбинация?

4. @ildjarn: Я сравниваю здесь таблицы 2 db, поэтому для получения сводки отчетов я хочу, чтобы была возможность создавать отдельные повторные списки для нескольких разных условий соответствия (например, Количество пересечений; a не в количестве b; b не в количестве a). Кстати, я думаю, что упаковка и распаковка значения может сработать (не уверен, почему это должно потребоваться, когда printfn правильно обрабатывает cvn) — мне нужно протестировать это дальше.

5. printfn обрабатывает recid правильно, потому что вы используете %A в качестве спецификатора формата, который работает с любым типом; Я подозреваю, что это не сработало бы, если бы вы использовали %d вместо спецификатора формата, что означает, что x.["PatientID"] фактически не возвращает int . Предполагая, что вы используете Visual Studio, не могли бы вы просто навести курсор rdict и посмотреть на тип во всплывающей подсказке?

Ответ №1:

Предполагая, что rdict это Dictionary<int*(bool*bool), _> , тогда для создания ResizeArray<int> я предлагаю:

 let reclist =
    (ResizeArray<_>(), rdict.Keys)
    ||> Seq.fold(fun list (id,_) -> list.Add id; list)
  

Кроме того, Dictionary<int*(bool*bool), _> мне кажется странным. Почему бы и нет Dictionary<int*bool*bool, _> ? Т.е. зачем вставлять bool пару в качестве второго кортежа? Если вы внесете это изменение, то можно было бы вызвать rdict.Add таким образом:

 rdict.Add ((x.["PatientID"], true, true), ldiff)
  

И reclist вместо этого было бы:

 let reclist =
    (ResizeArray<_>(), rdict.Keys)
    ||> Seq.fold(fun list (id,_,_) -> list.Add id; list)
  

РЕДАКТИРОВАТЬ: В своем комментарии вы упомянули о желании создавать отдельные ResizeArray s на основе разных комбинаций двух bool s в Dictionary ключе. Вот одна мысль о том, как это сделать:

 let reclistOnlyA, reclistOnlyB, reclistBoth, reclistNeither =
    ((ResizeArray<_>(), ResizeArray<_>(), ResizeArray<_>(), ResizeArray<_>()), rdict.Keys)
    ||> Seq.fold(fun (a, b, both, neither as lists) (id, bools) ->
        (match bools with
         | true, false  -> a
         | false, true  -> b
         | true, true   -> both
         | false, false -> neither).Add id
        lists)
  

Комментарии:

1. Ключ dict эволюционировал из начальной версии, в которой в качестве значения использовался только recid. Логически я рассматриваю логические значения несколько иначе, чем recid, поэтому я думаю, что поместил их в отдельный вложенный кортеж. Я вижу некоторые преимущества вашего подхода — спасибо за вашу помощь здесь на данный момент.

2. @BrendanC : Эффективность (уменьшение количества выделений) — единственное реальное преимущество int*bool*bool over int*(bool*bool) . Стилистически то, что у вас есть, кажется мне странным, но если для вас это имеет смысл, я не вижу в этом никакого существенного вреда. :-]

Ответ №2:

Для полноты картины и дальнейшего использования я просто хотел опубликовать свои результаты, основанные на дальнейшем тестировании. Путем упаковки / распаковки я смог успешно изменить свой ранее опубликованный код.

Надеюсь, это кому-нибудь еще пригодится в будущем.

     // Add initial value note the box call here
diff_dict.Add( (box x.["PatientID"],(true,true) ),ldiff) 


let reclist = new ResizeArray<int32>()
for KeyValue(k,v) in rdict do
    //printfn "Difference Dictionary - Key: %A; Value: %A; " k v 
    match k with
        // extract the result - note the 'unbox' call
        | (recid,(true,false)) ->  let n:int32 = unbox recid
                                   reclist.Add(n)