代码之家  ›  专栏  ›  技术社区  ›  Chris

不同的ValueTracker在Manim中返回相同的值

  •  1
  • Chris  · 技术社区  · 1 年前

    我正在尝试放置多个 DecimalNumber s,每个都有一个随机的起始值,所以当我使用 ValueTracker 在它们中的每一个上,它们似乎都会输出任意的排列。然而,当我实现此代码时:

    all_d, all_t = [], []
    
    for tile in tiles:
        start_number = random()
        k = ValueTracker(start_number)
        updateFunction = lambda i: i.set_value(k.get_value())
        d = DecimalNumber(num_decimal_places=2).add_updater(updateFunction)
        d.move_to(tile)
        all_d.append(d)
        all_t.append(k)
    
    print([k.get_value() for k in all_t]) # OUTPUT ValueTracker starting values
    
    self.play(*[Create(d) for d in all_d])
    self.play(*[k.animate.set_value(0) for k in all_t], run_time = 5)
    

    print语句输出随机数: [0.27563412131212717,..., 0.9962578393535727] ,但所有 小数位数 屏幕上的s输出相同的值,以1.00开始,以0.00结束。如果在所有代码之后添加相同的print语句,则会导致所有0: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

    我尝试添加: k.set_value(start_number) 在初始化 ValueTracker ,但事实证明这是徒劳的。我也尝试过使用以下代码:

    all_d, all_t = [], []
    
    for i, tile in enumerate(tiles):
        start_number = random()
        d = DecimalNumber(num_decimal_places=2)
        d.move_to(tile)
        all_d.append((i, d, start_number))
        all_t.append(ValueTracker(start_number))
    
    self.play(*[all_t[i].animate.set_value(start_number + 1) for i, _, start_number in all_d],
              *[UpdateFromFunc(d, lambda m: m.set_value(all_t[i].get_value())) for i, d, _ in all_d],
              run_time = 5)
    

    但他们仍然匹配,从1.00到2.00。

    我该如何补救?

    1 回复  |  直到 1 年前
        1
  •  3
  •   JLDiaz    1 年前

    这是一个棘手的问题,因为lambda正在访问外部变量。特别是,看看这两条线,在你的 for 循环:

            # for ...
                k = ValueTracker(start_number)
                updateFunction = lambda i: i.set_value(k.get_value())
                # ...
                all_t.append(k)
    

    在…内 lambda 您使用 k ,以及 k 只不过是对外部变量的引用。在定义lambda时 k 未读取。但是,当lambda稍后被manim作为更新器函数执行时,它会被读取。

    问题是这个lambda以及在循环中创建的所有lambda, 参考相同 k ,因为 k 是lambdas的一种“全局”变量,因为它是它们的外部变量。

    的值 k 循环的每次迭代中都会发生更改,但这不会影响Lambda,直到它们运行为止。当时 k 是对最后一个的引用 ValueTracker() 在循环中创建。所有其他ValueTracker都存储在列表中 all_t 但是 k 仅指最后一个。

    这就解释了为什么,当你循环 all_t 要打印值,您会得到不同的值,但更新程序(lambdas)使用所有相同的值(列表中的最后一个)。

    解决方案

    这个解决方案看起来像是一个破解,但如果你仔细想想,它实际上是完全合理的:

                k = ValueTracker(start_number)
                updateFunction = lambda i, k=k: i.set_value(k.get_value())
    

    注意小的添加 k=k 在lambda定义中。这是干什么的?

    • 它声明了一个名为 k 对于lambda。所以当你使用 k 在内部,它不再指外部变量,而是指特定lambda的局部变量(参数)。每个lambda都有自己的 k
    • 它给它一个默认值,因为当lambda作为更新程序调用时,manim不会将任何值传递给 k ,仅限于 i
    • 默认值为 k 在循环的那个迭代 .Python计算指定默认值的表达式,该计算的结果将用作默认值。

    换句话说,在 k=k 这个 k 左边是lambda的局部变量。这个 k 右侧的替换为的当前值 k 并分配给内部 k 作为默认值。

    这解决了你的问题。