在 Reasonml 中迭代记录键和值的最佳实践是什么?
What is the best practice of iterating record keys and values in Reasonml?
我是 ReasonML 的新手,但我通读了大部分官方文档。我可以为此进行随意的试验和错误,但由于我现在需要在 ReasonML 中编写代码,我想知道迭代 reason record[=13= 的键和值的最佳实践] 类型。
也许我不理解问题或用例。但据我所知,没有办法遍历 key/value 对记录。您可能想要使用不同的数据模型:
- 散列tablehttps://caml.inria.fr/pub/docs/manual-ocaml/libref/Hashtbl.html
- Js.Dict(如果您在 bucklescript/ReScript 工作)https://rescript-lang.org/docs/manual/latest/api/js/dict
- 元组列表
通过记录,所有键和值类型都是已知的,因此您只需编写代码来处理每个键和值类型,无需迭代。
我完全同意@Shawn 你应该使用更合适的数据结构。例如,元组列表是传递 user-defined 组同类 key/value 对的一种很好且简单的方法:
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")])]),
])
或者您可以使用对象而不是记录,这似乎是您真正想要的。
对于记录,record 需要 pre-defined 记录类型:
type record = {
foo: int,
bar: string,
};
这就是您构建它们的方式:
let value = {
foo: 42,
bar: "baz",
};
另一方面,Objects 是结构类型的,这意味着它们不需要 pre-defined 类型,并且您构造它们的方式略有不同:
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
这是一个函数,它接受任何对象和 returns 具有 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
来安全地获取实际的底层数据类型,类似于在 JavaScript 中使用 typeof
:
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 中的对象,并且我们不对值的类型做出任何假设。如果我们尝试将它与原始类型一起使用,我们只会得到一个空数组,如果我们尝试将它与数组一起使用,我们将获得索引作为键。
但是因为记录需要 pre-defined 这不会很有用。综上所述,我仍然建议使用元组列表。
注意:这使用了 ReasonML 语法,因为这是您要求的,但参考了 ReScript 文档,它使用了稍微不同的 ReScript 语法,因为 BuckleScript 文档已被删除(是的,它是现在一团糟,我知道。希望它最终会有所改善。)
我是 ReasonML 的新手,但我通读了大部分官方文档。我可以为此进行随意的试验和错误,但由于我现在需要在 ReasonML 中编写代码,我想知道迭代 reason record[=13= 的键和值的最佳实践] 类型。
也许我不理解问题或用例。但据我所知,没有办法遍历 key/value 对记录。您可能想要使用不同的数据模型:
- 散列tablehttps://caml.inria.fr/pub/docs/manual-ocaml/libref/Hashtbl.html
- Js.Dict(如果您在 bucklescript/ReScript 工作)https://rescript-lang.org/docs/manual/latest/api/js/dict
- 元组列表
通过记录,所有键和值类型都是已知的,因此您只需编写代码来处理每个键和值类型,无需迭代。
我完全同意@Shawn 你应该使用更合适的数据结构。例如,元组列表是传递 user-defined 组同类 key/value 对的一种很好且简单的方法:
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")])]),
])
或者您可以使用对象而不是记录,这似乎是您真正想要的。
对于记录,record 需要 pre-defined 记录类型:
type record = {
foo: int,
bar: string,
};
这就是您构建它们的方式:
let value = {
foo: 42,
bar: "baz",
};
另一方面,Objects 是结构类型的,这意味着它们不需要 pre-defined 类型,并且您构造它们的方式略有不同:
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
这是一个函数,它接受任何对象和 returns 具有 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
来安全地获取实际的底层数据类型,类似于在 JavaScript 中使用 typeof
:
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 中的对象,并且我们不对值的类型做出任何假设。如果我们尝试将它与原始类型一起使用,我们只会得到一个空数组,如果我们尝试将它与数组一起使用,我们将获得索引作为键。
但是因为记录需要 pre-defined 这不会很有用。综上所述,我仍然建议使用元组列表。
注意:这使用了 ReasonML 语法,因为这是您要求的,但参考了 ReScript 文档,它使用了稍微不同的 ReScript 语法,因为 BuckleScript 文档已被删除(是的,它是现在一团糟,我知道。希望它最终会有所改善。)