#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
overint*(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)