Я новичок в ReasonML, но прочитал большинство официальных документов. Для этого я мог бы пройти через случайные пробы и ошибки, но поскольку мне нужно писать коды в ReasonML прямо сейчас, я хотел бы знать лучшие практики итерации ключей и значений типов записи причины.
Как лучше всего повторять ключи и значения записей в Reasonml?
Ответы (2)
Я полностью согласен с @Shawn, что вам следует использовать более подходящую структуру данных. Например, список кортежей - это хороший и простой способ передать определенный пользователем набор однородных пар ключ / значение:
fooOnThis([
("test1", ["a", "b", "c"]),
("test2", ["c"]),
])
Если вам нужны разнородные данные, я бы предложил использовать вариант для указания типа данных:
type data =
| String(string)
| KvPairs(list((string, data)));
fooOnThis([
("test1", [String("a"), String("b"), String("c")]),
("test2", [String("c"), KvPairs([("innerTest", "d")])]),
])
В качестве альтернативы вы можете использовать объекты вместо записей, что похоже на то, что вы действительно хотите.
Для записи: запись требует предварительно определенного типа записи:
type record = {
foo: int,
bar: string,
};
и вот как вы их строите:
let value = {
foo: 42,
bar: "baz",
};
объекты, с другой стороны, структурно типизированы, что означает, что они не требуют предопределенный тип, и вы создаете их немного иначе:
let value
: {. "foo": int, "bar": string }
= {"foo": 42, "bar": "baz"};
Обратите внимание, что ключи - это строки.
С объектами вы можете использовать Js.Obj.keys
для получения ключей. :
let keys = Js.Obj.keys(value); // returns [|"foo", "bar"|]
Теперь проблема заключается в получении значений. Не существует Js.Obj
API для получения значений или записей, потому что это было бы либо ненадежно, либо очень непрактично. Чтобы продемонстрировать это, попробуем сделать это сами.
Мы легко можем написать собственную привязку к Object.entries
:
[@bs.val] external entries: Js.t({..}) => array((string, _)) = "Object.entries";
entries
это функция, которая принимает любой объект и возвращает массив кортежей с string
ключами и значениями типа, который будет выведен в зависимости от того, как мы их используем. Это небезопасно, потому что мы не знаем, каковы фактические типы значений, и не особенно практично, поскольку они будут однородно типизированы. Например:
let fields = entries({"foo": 42, "bar": "baz"});
// This will infer the value's type as an `int`
switch (fields) {
| [|("foo", value), _|] => value + 2
| _ => 0
};
// This will infer the value's type as an `string`, and yield a type error
// because `fields` can't be typed to hold both `int`s and `string`s
switch (fields) {
| [|("foo", value), _|] => value ++ "2"
| _ => ""
};
Вы можете использовать любое из этих switch
выражений (с неожиданными результатами и возможными сбоями во время выполнения), но не оба вместе, поскольку в Reason нет распакованного string | int
типа.
Чтобы обойти это, мы можем сделать значение абстрактным типом и использовать Js.Types.classify
, чтобы безопасно получить фактический базовый тип данных, аналогично использованию typeof
в JavaScript:
type value;
[@bs.val] external entries: Js.t({..}) => array((string, value)) = "Object.entries";
let fields = entries({"foo": 42, "bar": "baz"});
switch (fields) {
| [|("foo", value), _|] =>
switch (Js.Types.classify(value)) {
| JSString(str) => str
| JSNumber(number) => Js.Float.toString(number)
| _ => "unknown"
}
| _ => "unknown"
};
Это совершенно безопасно, но, как видите, не очень практично.
Наконец, мы можем немного изменить это, чтобы безопасно использовать и с записями, полагаясь на тот факт, что записи внутренне представлены как объекты JavaScript. Все, что нам нужно сделать, это не ограничивать entries
объектами:
[@bs.val] external entries: 'a => array((string, value)) = "Object.entries";
let fields = keys({foo: 42, bar: 24}); // returns [|("foo", 42), ("bar", 24)|]
Это по-прежнему безопасно, потому что все значения являются объектами в JavaScript, и мы не делаем никаких предположений о типах значений. Если мы попытаемся использовать это с примитивным типом, мы просто получим пустой массив, а если мы попытаемся использовать его с массивом, мы получим индексы как ключи.
Но поскольку записи должны быть предварительно определены, это не будет очень полезно. Итак, все это говорит о том, что я все же предлагаю перейти к списку кортежей.
Примечание: здесь используется синтаксис ReasonML, поскольку это то, что вы просили, но относится к документации ReScript, в которой используется немного другой синтаксис ReScript, поскольку документация BuckleScript была удалена (да, сейчас беспорядок, я знаю. Надеюсь, со временем это улучшится.)
Возможно, я не понимаю вопрос или вариант использования. Но, насколько мне известно, нет возможности перебирать пары ключ / значение в записи. Вы можете использовать другую модель данных:
- хэш-таблица https://caml.inria.fr/pub/docs/manual-ocaml/libref/Hashtbl.html
- Js.Dict (если вы работаете в bucklescript / ReScript) https://rescript-lang.org/docs/manual/latest/api/js/dict
- список кортежей
С помощью записи известны все ключи и типы значений, поэтому вы можете просто написать код для обработки каждого из них, без необходимости повторения.
keys
, которая перечисляет все ключи, и функцию get
, чтобы получить значение определенного ключа.
- person Shawn; 24.09.2020