Python __repr__ 方法:如何编写等效的JavaScript代码?

3

我正在学习算法和数据结构的入门课程。教师使用的语言是Python;我将代码示例转换为JavaScript。目前为止,一切都很顺利。

我正在处理链表。教师使用Python的__repr__()方法来测试代码。经过数天的尝试和错误,我有了一个可行的JS解决方案,但它与Python代码并不完全相同。我想知道是否有更好的实现JS代码的方法,我提供了JS代码和Python代码。

Python

# class LinkedList and its methods are presumed to exist

def __repr__(self):
  
  nodes = []
  current = self.head

  while current:
    if current is self.head:
      nodes.append("[Head: %s]" % current.data)
    elif current.next_node is None:
      nodes.append("[Tail: %s]" % current.data)
    else
      nodes.append("[%s]" % current.data)

    current = current.next_node
  return '-> '.join(nodes)

# running script
>>> l = LinkedList()
>>> l.add(1)
>>> l.add(2)
>>> l.add(3)
>>> l
[Head: 3]-> [2]-> [Tail: 1]  # output
>>>

JS

// class LinkedList and its methods are presumed to exist

repr () {
  
  let nodes = "";
  let current = this.head;

  while (current) {
    if (current === this.head) {
      nodes = `Head: ${current.data}-> `;
    } else if (current.nextNode === null) {
      nodes += `Tail: ${current.data}`;
    } else {
      nodes += `${current.data}-> `;
    }
    current = current.nextNode;
  }
  return nodes;

// running the script
let l = LinkedList();
l.add(1);
l.add(2);
l.add(3);

let result = l.repr();
console.log(result);  // Head: 3-> 2-> Tail: 1

再次说明,这两个代码片段只在完整的链表算法中才能运行,但它们确实有效。
我尝试过使用JS的toString()、append()和appendChild()方法,但是我很难理解如何最好地使用它们,特别是后两个方法修改了DOM。我相信有一种更好的方式来实现JS等价于Python的__repr__()方法;我想知道如何实现它。

对我来说,这看起来相当合理。有几个小问题:始终在使用变量之前定义它们(使用 constlet),并且 nodes 应该初始化为空字符串,而不是数组,但我没有看到任何值得改进的结构性改进。 - CertainPerformance
@CertainPerformance:感谢您的认可投票。关于定义; 这是一个疏忽(在Py和JS之间工作时很容易发生,我想)。 - Den
1个回答

2
一个更好的实现方式是使用toString方法。当需要转换为字符串时,此方法会被隐式调用。Python实际上有两种方法,它们的目的略有不同:__repr____str__。在JavaScript中没有这样的区别。
此外,我们应该意识到Python的print会隐式调用__repr__,而这并不是console.log的工作方式。因此,在使用console.log时,您需要强制进行字符串转换。
以下是最直接翻译给定的Python代码的方式(我添加了运行脚本所需的类):

class Node {
    constructor(data, next=null) {
        this.data = data;
        this.next_node = next;
    }
}

class LinkedList {
    constructor() {
        this.head = null;
    }
    add(data) {
        this.head = new Node(data, this.head);
    }
    toString() {
        let nodes = [];
        let current = this.head;
        while (current) {
            if (current === this.head) {
                nodes.push(`[Head: ${current.data}]`);
            } else if (current.next_node === null) {
                nodes.push(`[Tail: ${current.data}]`);
            } else {
                nodes.push(`[${current.data}]`);
            }
            current = current.next_node;
        }
        return nodes.join('-> ');
    }
}

// running script
let l = new LinkedList();
l.add(1);
l.add(2);
l.add(3);
// Force conversion to string
console.log(`${l}`); // [Head: 3]-> [2]-> [Tail: 1]

就我个人而言,我会做出以下更改(未反映在Python版本中):

  • 生成没有“Head”和“Tail”等其他“装饰”的输出。这太啰嗦了。只需输出分隔的值。
  • 使列表实例可迭代,实现Symbol.iterator方法(在Python中:__iter__)。然后将其用于实现toString方法。
  • 允许列表构造函数使用任意数量的值来填充列表。

这导致以下版本:

class Node {
    constructor(data, next=null) {
        this.data = data;
        this.next = next;
    }
}

class LinkedList {
    constructor(...values) { // Accept any number of values
        this.head = null;
        // Populate in reverse order
        for (let data of values.reverse()) this.add(data);
    }
    add(data) {
        this.head = new Node(data, this.head);
    }
    // Make lists iterable
    *[Symbol.iterator]() {
        let current = this.head;
        while (current) {
            yield current.data;
            current = current.next;
        }
    }
    toString() {
        // Array.from triggers the above method
        return Array.from(this).join("→");
    }
}

// Provide the desired values immediately:
let l = new LinkedList(3, 2, 1);
console.log(`${l}`); // 3→2→1


trincot:你实际上是在使用JS的toString()方法,还是这是你给LinkedList类方法起的名字?很想知道。谢谢。 - Den
当JS需要将对象转换为字符串(例如在模板文字中使用时),它会检查该对象是否具有(或继承)toString方法。如果是这样,它将被调用。因此,由于我们已经在原型(类)上定义了toString方法,它将被调用。如果我们没有定义它,JS将会找到预定义的Object.prototype.toString并执行它。 - trincot
trincot: 我明白了。这也可以解释为什么在类方法中使用其他Object.Prototypes,例如pushpop。我确实发现JavaScript方法的名称被使用有点令人困惑,并且是否存在冲突也不确定;因此,我选择创建原始方法名称。这可能增加了调用方法的额外步骤,但对我来说以这种方式实现我的类方法是有意义的。 - Den
真的,但是同样的论点也可以用于Python的__repr__。就像Python在实现时隐式地调用__repr__一样,toString也以类似的方式被调用(尽管JS和Python在提供继承方面有所不同)。我没有完全理解你对pushpop的意思:它们是Array原型上的本地方法(而不是Object原型),并且它们从不被隐式调用。 - trincot
事实上,Python中的list实例的append方法对应于JavaScript中Array实例的push方法。 - trincot
显示剩余2条评论

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