为啥不接受 Map 构造函数的可迭代元组数组?

Posted

技术标签:

【中文标题】为啥不接受 Map 构造函数的可迭代元组数组?【英文标题】:Why doesn't accept the Map constructor an Array of iterable tuples?为什么不接受 Map 构造函数的可迭代元组数组? 【发布时间】:2017-10-21 07:18:27 【问题描述】:

我在 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,而不接受内部对。我错过了什么?有没有其他方法可以实现转换?

【问题讨论】:

每次创建Tuple时创建两个函数,您支持什么设计目标? 不变性;避免使用Arrays 来存储各种类型的相关数据(例如[a, b])。 Array 是一个集合,因此应该是 [a] 类型。 不变性不需要您为每个 Tuple 创建多个函数。 哦,是的,你是对的,它是教堂编码的。当然,不必如此。 “它是教堂编码的” 有趣! 【参考方案1】:

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

如果我们查看the spec,如果它支持属性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"

【讨论】:

这很有意义。谢谢!【参考方案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"));

【讨论】:

【参考方案3】:

我在 Javascript 中创建了一个元组类型,它实现了Iterable 协议。

不要。元组具有固定长度的独特属性,每个元素都有(可以有)自己的类型,而(可迭代的)集合由 same 类型的多个元素组成。当然 JavaScript 不关心类型(所有数组都是异构的),但是作为程序员的你应该关心。

为什么Map 只接受外部复合类型的Iterable,而不接受内部对?

出于完全相同的原因。元素是 pairs 或二元组,表示为数组(因为缺少另一种数据结构,并且不必像迭代器结果那样引入一个简单的数据结构)。它们总是有两个元素,可以通过[0][1] 访问(它们甚至不需要.length!),绝对没有理由重复它们。

【讨论】:

我知道元组通常是不可折叠的。我只是让它们可迭代,以便它们可以很好地与Array.fromMap 一起使用。但是,我不明白 Map 构造函数是如何在幕后工作的。现在它很有意义! @ftor Array.from 也适用于具有整数属性和 .length 的对象。不过,我不明白为什么或如何从元组创建 Map 也许我在这里走错了路。我认为从[(k, v)] 转换为Map k v 以及反之亦然是明智之举。但是,我对元组没有太多经验。 是的,我以为你的意思是 new Map(Tuple([k1, v1], [k2, v2], …)) 元组需要是可迭代的。对于Array.from(Tuple(…))new Map([Tuple(k1, v1), Tuple(k2, v2), …]),您只需要01length 三个属性。 对不起,没有。我只是假设“accepts an Iterable”也指的是内部对 [[k, v]]。但是,当这些对的结构简单明了且众所周知时,为什么要这样做。我将尝试为我的下一个问题提供类型签名。无论如何,我已经习惯为我的所有功能定义它们。事实证明,当我在表达类型信号方面遇到困难时,我经常会最终丢弃相应的函数。它是一个函数是否有用的一个很好的指标——即使在无类型语言中也是如此。

以上是关于为啥不接受 Map 构造函数的可迭代元组数组?的主要内容,如果未能解决你的问题,请参考以下文章

c#中泛型类构造函数重载赋值时为啥不接受null?对其赋空值应给怎么做?

在 C# String 构造函数 String(Char*) 中,为啥构造函数不期望指向字符数组的指针?

为啥 mxml 不支持组件构造函数?

映射和元组

Set和Map数据结构介绍及使用场景分析

为啥复制构造函数应该在 C++ 中通过引用来接受它的参数?