为什么 Map 构造函数不接受可迭代元组的数组作为参数?

3

我在JavaScript中创建了一个元组类型,实现了Iterable协议。现在将其转换为Map应该很容易:

const Tuple = (...args) => {
  const Tuple = f => f(...args);
  Tuple[Symbol.iterator] = () => args[Symbol.iterator]();
  return Tuple;
};

new Map([Tuple(1, "a"), Tuple(2, "b")]); // Map {undefined => undefined}

相反,我必须手动进行转换:

const Tuple = (...args) => {
  const Tuple = f => f(...args);
  Tuple[Symbol.iterator] = () => args[Symbol.iterator]();
  return Tuple;
};

const toArray = tx => tx((...args) => args);

const Map_ = (...pairs) => {
  return new Map(pairs.map(pair => toArray(pair)));
};

const map = Map_(Tuple(1, "a"), Tuple(2, "b"));

console.log(map.get(1), map.get(2)); // "a" "b"

看起来Map只接受外层组合类型的Iterable,而不是内部键值对的Iterable。我错过了什么吗?是否有其他方法可以实现转换?


每次创建“元组”时,您通过创建两个函数支持哪些设计目标? - T.J. Crowder
不可变性;避免使用Array来存储各种类型的相关数据(例如[a,b])。 Array是一个集合,因此应该是类型为[a] - user6445533
不可变性并不要求您为每个“元组”创建多个函数。 - T.J. Crowder
1
哦,是的,你说得对,它是教堂编码。当然,它不一定非得这样。 - user6445533
1
"这是经过编码的代码" 有意思! - T.J. Crowder
3个回答

4

Map的构造函数定义为接受任何键值对象的可迭代对象,其中键值对象被定义为属性0是键,属性1是值的对象。它并没有说明允许使用键值对可迭代对象。(如果允许并且定义了它将恰好调用迭代器两次,那就很酷了...但这不是他们所做的。:-) 而且这会变得更复杂...)

如果我们查看规范,如果支持属性0(用于键)和1(用于值),则您的Tuple将起作用。例如:

// (Defined this way because you seemed to want a non-constructor)
const TupleMethods = {
  get "0"() {
    return this.key;
  },
  get "1"() {
    return this.value;
  }
};
const Tuple = (key, value) => {
  const t = Object.create(TupleMethods);
  t.key = key;
  t.value = value;
  return t;
}
// Usage:
const m = new Map([
  Tuple("one", "uno"),
  Tuple("two", "due"),
  Tuple("three", "tre")
]);
console.log(m.get("two")); // "due"

在您的评论中提到了不可变性。可能会这样表达:

// (Defined this way because you seemed to want a non-constructor)
const TupleMethods = {};
Object.defineProperties(TupleMethods, {
    "0": {
        get() {
            return this.key;
        }
    },
    "1": {
        get() {
            return this.value;
        }
    }
});
const Tuple = (key, value) => {
    const t = Object.create(TupleMethods);
    Object.defineProperties(t, {
        key: {
            value: key
        },
        value: {
            value: value
        }
    });
    return t;
};

// Usage:
const m = new Map([
    Tuple("one", "uno"),
    Tuple("two", "due"),
    Tuple("three", "tre")
]);
console.log(m.get("two")); // "due"


1
那很有道理。谢谢! - user6445533

0
我在Javascript中创建了一个元组类型,实现了Iterable协议。
不要这样做。元组具有固定长度的独特属性,每个元素都可以有自己的类型,而(可迭代)集合由多个相同类型的元素组成。当然,JavaScript不关心类型(所有数组都是异构的),但作为程序员的你应该关心。
为什么Map只接受外部复合类型的Iterable,而不接受内部对吗?
出于完全相同的原因。这些元素是一对或两个元组,表示为数组(由于没有其他数据结构,并且不需要像迭代器结果那样引入一个简单的结构)。它们始终恰好有两个元素,可以通过[0][1]访问(它们甚至不需要.length!),并且绝对没有理由迭代它们。

我知道元组通常是不可折叠的。我只是让它们可迭代,以便它们可以与 Array.fromMap 很好地配合使用。然而,我不理解 Map 构造函数在内部是如何工作的。现在我完全明白了! - user6445533
@ftor Array.from 也适用于具有整数属性和 .length 的对象。我不明白为什么或如何从元组创建一个 Map - Bergi
也许我在这条路上走错了。我认为将[(k, v)]转换为Map k v,反之亦然,是一件明智的事情。然而,我对元组并没有太多经验。 - user6445533
是的,我以为你指的是new Map(Tuple([k1, v1], [k2, v2], …)),这需要元组可迭代。对于Array.from(Tuple(…))new Map([Tuple(k1, v1), Tuple(k2, v2), …]),你只需要三个属性01length - Bergi
抱歉,不是的。我只是假设“_accepts an Iterable_”也适用于内部的键值对[[k,v]]。但是为什么要这样做呢?因为这些键值对的结构是简单明了且众所周知的。我会尝试为我的下一个问题提供类型签名。无论如何,我已经习惯为所有函数定义它们。结果证明,当我在表达类型签名方面遇到麻烦时,我通常最终会放弃相应的函数。这是一个很好的指标,无论在有类型的语言还是无类型的语言中,都可以判断一个函数是否有用。 - user6445533

-2

const Tuple = (...args) => Object.entries({[args.shift()]: args.shift()});

const map = new Map(Tuple(0, "a").concat(Tuple(1, "b")));

console.log(map.get("0"), map.get("1"));


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接