F# Deedle доступ к строке
Это основной вопрос, но я не мог найти простой ответ, читая учебник
Предположим, у меня есть эта простая рамка
type Person =
{ Name:string; Age:int; Countries:string list; }
let peopleRecds =
[ { Name = "Joe"; Age = 51; Countries = [ "UK"; "US"; "UK"] }
{ Name = "Tomas"; Age = 28; Countries = [ "CZ"; "UK"; "US"; "CZ" ] }
{ Name = "Eve"; Age = 2; Countries = [ "FR" ] }
{ Name = "Suzanne"; Age = 15; Countries = [ "US" ] } ]
// Turn the list of records into data frame
let peopleList = Frame.ofRecords peopleRecds
// Use the 'Name' column as a key (of type string)
let people = peopleList |> Frame.indexRowsString "Name"
Как мне получить доступ к значению строки для Джо? (как запись, кортеж или любой другой формат)
я попробовал это
getRow "Joe" people;;
Остановлен из-за ошибки System.Exception: операция не может быть завершена из-за более ранней ошибки Ограничение значения. Значение 'it' было выведено, чтобы иметь универсальный тип val it: Series Либо определите 'it' как простой термин данных, сделайте его функцией с явными аргументами или, если вы не собираетесь использовать его как универсальный, добавьте тип аннотаций. в 3,0
РЕДАКТИРОВАТЬ: спасибо за ответ, все же я хотел бы знать, почему мой синтаксис является неправильным, потому что я думаю, что я уважал подпись
val it :
('a -> Frame<'a,'b> -> Series<'b,'c>) when 'a : equality and 'b : equality
2 ответа
Я отвечу на вторую половину вашего вопроса, почему вы получили ошибку "ограничение стоимости". Если вы ищете [f#] value restriction
в переполнении стека вы найдете множество ответов, которые могут вас запутать или не запутать. Но действительно короткая версия такова: F# построен поверх платформы.Net, а.Net налагает определенные ограничения. В частности, функции могут быть универсальными, но значения не могут быть универсальными. Так что вы можете сделать это:
let f<'TData> (a:'TData) = printfn "%A" a
но вы не можете сделать это:
let (a:'TData) = Unchecked.defaultof<'TData>
Определение функции хорошо, потому что базовая структура.Net знает, как обрабатывать универсальные функции. Но вы не можете иметь общие значения в.Net; любое значение должно быть определенного типа.
(Примечание: я написал <'TData>
в f
определение явно, но я не должен был: я мог бы просто написать let f (a:'TData) = printfn "%A" a
и универсальность f
все равно был бы понят. Я мог бы даже написать let f a = printfn "%A" a
и сделал бы то же самое)
Теперь давайте посмотрим на ошибку, которую вы получили: значение "it" было выведено, чтобы иметь универсальный тип val it : Series<string,obj>
". Если вы посмотрите на функцию подписи getRow
что вы разместили, это выглядит так:
('a -> Frame<'a,'b> -> Series<'b,'c>)
Когда ты назвал это как getRow "Joe" people
компилятор F# смог сделать вывод, что тип 'a
было string
(потому что параметр "Joe"
это string
). И потому что второй аргумент people
это Frame<string,string>
компилятор F# смог сделать вывод, что тип 'b
был также string
, Но результатом этого вызова функции является Series<'b,'c>
и до сих пор компилятор F# ничего не знает о том, что 'c
будет. И так как ты побежал getRow "Joe" people
в интерактивном REPL F# он пытался сохранить результат того, что вы ввели в качестве значения имени it
(F# интерактивный REPL всегда предоставляет значение предыдущего выражения как it
) - но так как единственный тип, который это знало, был Series<string,'c>
, F# не мог понять, какой конкретный тип присваивать значению it
, Из вашего кода я знаю, что тип 'c
был Person
запись, но F# компилятор не мог знать это только от одного вызова getRow
из-за того, как getRow
функция набрана.
Существует два способа устранения этой ошибки ограничения значения:
Один из способов решить эту проблему - передать результат
getRow
в другую функцию, которая позволила бы компилятору F# выводить конкретный тип его результата. К сожалению, поскольку я не очень хорошо знаю Дидла, я не могу дать вам хороший пример здесь. Может быть, кто-то еще придумает и прокомментирует этот ответ, и я отредактирую его. Это будет выглядеть так:getRow "Joe" people |> (some Deedle function)
Но я не знаю, какую функцию Deedle использовать в моем примере: это должна быть функция, которая принимает
Series
и делает какой-то конкретный расчет с ним таким образом, что позволит F# сделать вывод, что этоSeries<string,Person>
, Извините, это не хороший пример, но я все равно оставлю его на случай, если это поможет.Второй способ, которым вы могли бы решить эту ошибку, - указать тип получаемого вами значения. В F# вы делаете это с
: (type)
синтаксис, например:getRow "Joe" people : Series<string,Person>
Или, поскольку компилятор F# имеет достаточно информации, чтобы вывести
string
часть этого типа, вы могли бы также написать:getRow "Joe" people : Series<_,Person>
Когда ты пишешь
_
в сигнатуре типа вы говорите компилятору F# "Вы выясните, что это за тип". Это работает только тогда, когда у компилятора F# достаточно информации, чтобы правильно вывести этот тип, но часто это удобное сокращение, когда сигнатуры типов будут большими и громоздкими.
Оба этих подхода позволили бы решить вашу непосредственную проблему, избавиться от ошибки "ограничения ценностей" и позволить вам продолжить работу.
Я надеюсь, что этот ответ поможет вам. Если это безнадежно смущает вас, дайте мне знать, и я посмотрю, смогу ли я объяснить, что вас смутило.
РЕДАКТИРОВАТЬ: В комментариях Солдальма спрашивает, может ли компилятор F# (который является однопроходным компилятором, который работает сверху вниз и слева направо), может выводить тип из прямого канала. Ответ - да, потому что выражение еще не закончено. Пока выражение не закончено, вывод типа F# (который основан на системе типов Хиндли-Милнера*) прекрасно подходит для переноса набора еще не разрешенных типов. И если типы разрешаются до завершения выражения, тогда выражение может разрешаться до определенного значения (или конкретной функции). Если типы еще не разрешены, когда выражение завершено, тогда оно должно преобразоваться в универсальное значение или функцию. И универсальные функции разрешены в.Net, но не универсальные значения, следовательно, ошибка "ограничения значений".
Чтобы увидеть это на практике, давайте рассмотрим пример кода. Скопируйте и вставьте следующий код в редактор F#, который позволяет навести курсор на имя переменной (или функции), чтобы увидеть ее тип. Я рекомендую VS Code с расширением Ionide-fsharp, поскольку он кроссплатформенный, но Visual Studio будет работать так же хорошо.
open System.Collections.Generic
let mkDict (key:'K) = new Dictionary<'K,'V>() // Legal
let getValueOrDefault (key:'a) (defaultVal:'b) (dict:Dictionary<'a,'b>) =
match dict.TryGetValue key with
| true,v -> v
| false,_ -> defaultVal
let d = mkDict "foo" // Error: value restriction
let bar = mkDict "foo" |> getValueOrDefault "foo" "bar" // Legal: type string
let five = mkDict "foo" |> getValueOrDefault "foo" 5 // Legal: type int
Перейдите и наведите курсор на каждую функцию и имя переменной, чтобы увидеть ее тип, или нажмите Alt+Enter, чтобы отправить объявление каждой функции или переменной в F# Interactive. (И как только вы увидели, что let d
строка выдает ошибку "value limit", закомментируйте ее, чтобы остальная часть кода была скомпилирована).
То, что здесь происходит, является хорошей демонстрацией того, как все это работает. mkDict
функция имеет два неразрешенных типа, 'K
а также 'V
так что оно должно быть общим. Но это нормально, потому что.Net не имеет проблем с общими функциями. (mkDict
на самом деле не очень полезен, так как на самом деле "выбрасывает" данные своего аргумента и ничего с ним не делает. Но это должен быть тривиальный пример, так что просто игнорируйте тот факт, что это отчасти бесполезно.) getValueOrDefault
имеет два неразрешенных типа, 'a
а также 'b
, так что это также общая функция.
Тем не мение, let d = mkDict "foo"
не законно Здесь общий тип 'K
было решено, чтобы быть конкретным типом string
, но 'V
еще не было решено к тому времени, когда выражение завершено, так d
должно быть общим (это будет выглядеть d<'V>
в явно-общий синтаксис). Но d
это не функция (так как она не имеет параметров), это имя значения, а.Net не допускает общих значений.
Но в следующих двух строках выражение не будет завершено ко времени разбора компилятором mkDict "foo"
Таким образом, он еще не должен блокировать неизвестные типы. Он вполне может нести нерешенный тип 'V
в следующую часть выражения. И там, getValueOrDefault
функция имеет два конкретных типа, string
а также string
в первой строке и string
а также int
во второй строке. Потому что это 'b
тип соответствует 'V
введите от mkDict
поэтому F# может разрешить 'V
в обе строки. Так что bar
имеет тип string
, а также five
имеет тип int
,
* Скотт Влащин говорит, что его следует "точнее... назвать" алгоритмом Дамаса-Милнера W" ". Так как я сам не изучал это подробно, я верю его слову - но если вы заинтересованы в получении дополнительной информации, предоставленная мною ссылка на Википедию, вероятно, является достойной отправной точкой на полпути.
Я буду очень коротким, продвигая свой комментарий к ответу. Вам нужно использовать синтаксис, обратный тому, который вы пробовали: people.Rows.["Joe"]