Example #1
0
    def construct(self):
        # - Now, let's change this a bit and break it!
        # - Now modify it (a <= 0) and call it with foo(0).
        # - switch it out for the broken one, and highlight and note the change.
        code_scale = 0.75
        ss_good_code = CodeBlock(
            'Java',
            r"""
            public static double StrangeSqrt(double a) { 
                if (a < 0) { 
                    double b = StrangeSqrt(a * -1);
                    return b;
                } 
                return Math.sqrt(a);
            }
            """,
            code_scale=code_scale,
        )
        ss_good_code.to_edge(UP)
        self.add(ss_good_code)

        t1 = TextMobject(
            "Let's break this code in a very small way and\\\\see what happens..."
        )
        t1.next_to(ss_good_code, DOWN, buff=LARGE_BUFF)
        self.play(FadeIn(t1))
        self.wait()

        gl2 = ss_good_code.get_code().get_lines(2)
        ghr = SurroundingRectangle(gl2[3:6])
        self.play(ShowCreation(ghr))
        self.wait()

        ss_bad_code = CodeBlock(
            'Java',
            r"""
            public static double StrangeSqrt(double a) { 
                if (a <= 0) { 
                    double b = StrangeSqrt(a * -1);
                    return b;
                } 
                return Math.sqrt(a);
            }
            """,
            code_scale=code_scale,
        )
        ss_bad_code.to_edge(UP)
        bl2 = ss_bad_code.get_code().get_lines(2)
        bhr = SurroundingRectangle(bl2[3:7])
        self.play(
            ReplacementTransform(ss_good_code, ss_bad_code),
            ReplacementTransform(ghr, bhr),
        )
        self.wait()

        t2 = TextMobject(
            "This small change will have a big effect on one case: 0\\\\"
            "Let's run it and see.").next_to(ss_bad_code,
                                             DOWN,
                                             buff=LARGE_BUFF)
        self.play(
            FadeOut(t1),
            FadeIn(t2),
        )
        self.wait(duration=3)

        self.play(
            FadeOut(t2),
            FadeOut(bhr),
        )
        self.wait()

        # - Start stepping through this and see the stack start to grow forever.
        main_code = CodeBlock(
            'Java',
            r"""
            public static void main(String[] args) {
                double n = StrangeSqrt(0);
            }
            """,
            line_offset=7,
            code_scale=code_scale - 0.1,
        )
        frame_width = 3.5
        main_frame = StackFrame(main_code,
                                'main()',
                                9, ['n'],
                                width=frame_width)
        main_code.highlight_lines(9)
        VGroup(main_code, main_frame).arrange(RIGHT,
                                              buff=LARGE_BUFF).to_edge(DOWN)
        self.play(
            FadeInFromDown(main_frame),
            FadeInFromDown(main_code),
        )
        self.wait()

        ss_frame = StackFrame(ss_bad_code,
                              'StrangeSqrt(0)',
                              1, ['a', 'b'],
                              width=frame_width)
        ss_frame.next_to(main_frame, UP, buff=SMALL_BUFF)

        xi = main_code.pre_call(ss_bad_code, 1)
        self.play(
            *main_code.get_control_transfer_counterclockwise(xi),
            FadeInFrom(ss_frame, UP),
        )
        ss_bad_code.post_control_transfer(xi, self)
        self.wait()

        self.play(ss_bad_code.highlight_lines, 2, ss_frame.set_line, 2,
                  ss_frame.update_slot, 'a', 0)
        self.wait()

        self.play(ss_bad_code.highlight_lines, 3, ss_frame.set_line, 3)
        self.wait()

        def call_ss_again(stack_group, previous_frame, extras=None):
            wait_each_step = len(stack_group) < 3
            runtime = 1.0 if len(stack_group) < 2 else 0.5 if len(
                stack_group) < 6 else 0.20
            extra_anims = []
            if len(stack_group) > 2:
                extra_anims.append(
                    ApplyMethod(
                        stack_group.shift,
                        DOWN * previous_frame.get_height() +
                        DOWN * SMALL_BUFF))
            if extras:
                extra_anims.extend(extras)

            f = StackFrame(ss_bad_code,
                           'StrangeSqrt(0)',
                           1, ['a', 'b'],
                           width=frame_width)
            f.next_to(previous_frame, UP, buff=SMALL_BUFF)
            self.play(*extra_anims,
                      ss_bad_code.highlight_lines,
                      1,
                      f.set_line,
                      1,
                      FadeIn(f),
                      MaintainPositionRelativeTo(f, previous_frame),
                      run_time=runtime)
            if wait_each_step:
                self.wait()

            self.play(ss_bad_code.highlight_lines,
                      2,
                      f.set_line,
                      2,
                      f.update_slot,
                      'a',
                      0,
                      run_time=runtime)
            if wait_each_step:
                self.wait()

            self.play(ss_bad_code.highlight_lines,
                      3,
                      f.set_line,
                      3,
                      run_time=runtime)
            if wait_each_step:
                self.wait()
            stack_group.add(f)
            return f

        sg = VGroup(main_frame, ss_frame)
        curr_ss_frame = ss_frame
        for i in range(3):
            curr_ss_frame = call_ss_again(sg, curr_ss_frame)

        # - When will this program end? Never?
        t1 = TextMobject("Hrm... when is this going to end??").next_to(
            main_code, UP, buff=LARGE_BUFF)
        extra_text = [FadeIn(t1)]
        for i in range(4):
            curr_ss_frame = call_ss_again(sg, curr_ss_frame, extra_text)
            extra_text = None

        t2 = TextMobject("Never?").next_to(main_code, UP, buff=LARGE_BUFF)
        extra_text = [FadeOut(t1), FadeIn(t2)]
        for i in range(4):
            curr_ss_frame = call_ss_again(sg, curr_ss_frame, extra_text)
            extra_text = None

        sg.save_state()
        t3 = TextMobject("The stack is getting quite deep!").next_to(
            main_code, UP, buff=LARGE_BUFF)
        self.play(sg.scale, 0.20, {'about_edge': TOP}, FadeOut(t2), FadeIn(t3))
        self.wait(duration=2)
        self.play(sg.restore)

        extra_text = [FadeOut(t3)]
        for i in range(4):
            curr_ss_frame = call_ss_again(sg, curr_ss_frame, extra_text)
            extra_text = None

        # - Well, the stack is finite, so eventually you get a SO.
        #   Use the Java SO exception and show it hit and stop.
        so_frame = StackFrame(ss_bad_code,
                              'StrangeSqrt(0)',
                              1, ['a', 'b'],
                              width=frame_width)
        so_frame.slots().set_color(ORANGE)
        so_frame.header_line().set_color(ORANGE)
        so_frame.background_rect().set_fill(color=[BLACK, RED])
        so_frame.next_to(curr_ss_frame, UP, buff=SMALL_BUFF)
        so_exception = TextMobject(
            '\\texttt{Exception in thread "main"\\\\java.lang.StackOverflowError}',
        ).set_color(YELLOW).scale(0.75).next_to(main_code, UP, buff=LARGE_BUFF)
        self.play(sg.shift,
                  DOWN * so_frame.get_height() + DOWN * SMALL_BUFF,
                  ss_bad_code.get_current_highlight().set_color, ORANGE,
                  FadeIn(so_frame),
                  MaintainPositionRelativeTo(so_frame, curr_ss_frame),
                  Write(so_exception))
        sg.add(so_frame)
        self.wait(duration=2)

        ss_bad_code.generate_target()
        so_exception.generate_target()
        ss_bad_code.target.scale(0.75)
        g = VGroup(so_exception.target,
                   ss_bad_code.target).arrange(RIGHT, buff=LARGE_BUFF)
        g.to_edge(UP)
        sg.generate_target()
        sg.target.scale(0.15).next_to(g, DOWN).to_edge(RIGHT)
        t1 = TextMobject(
            "Space for the call stack is limited,\\\\so we can't make recursive calls forever!"
        )
        self.play(
            *[MoveToTarget(t) for t in [so_exception, ss_bad_code, sg]],
            FadeOutAndShiftDown(main_code),
            FadeInFromDown(t1),
        )
        self.wait(duration=2)

        t2 = TextMobject(
            "This program made {\\raise.17ex\\hbox{$\\scriptstyle\\sim$}}15,000 calls before crashing."
        )
        t4 = TextMobject(
            "\\textit{Java SE 13.0.1 on macOS Catalina 10.15.4}").scale(0.5)
        t2.next_to(t1, DOWN, buff=MED_LARGE_BUFF)
        t4.to_edge(DL)
        self.play(
            FadeIn(t2),
            FadeInFromDown(t4),
        )
        self.wait(duration=4)

        t5 = TextMobject('\\textit{Most common mistake with recursion.}')
        t6 = TextMobject("You'll see this a lot. Everyone does!")
        t6.next_to(t5, DOWN, buff=LARGE_BUFF)
        self.play(
            *[FadeOut(t) for t in [t1, t2, t4]],
            *[FadeIn(t) for t in [t5, t6]],
        )
        self.wait(duration=3)

        # Transition
        t1 = TextMobject(
            "Alright, let's look at a more interesting function...")
        self.play(
            *[FadeOut(o) for o in self.mobjects],
            FadeIn(t1),
        )
        self.wait()

        self.play(FadeOut(t1))
        self.wait()
Example #2
0
    def construct(self):
        t1 = TextMobject("Let's compute $x^n$").shift(UP)
        self.play(FadeIn(t1))
        self.wait()

        t2 = TextMobject("i.e., $2^4=16$, or $4^3=64$, etc.")
        t2.next_to(t1, DOWN, buff=LARGE_BUFF)
        self.play(FadeInFromDown(t2))
        self.wait(duration=2)

        t3 = TextMobject('Recall that')
        f1 = TexMobject('x^n=', 'x', '\\times', 'x \\times ... \\times x',
                        'x^{n-1}')
        f2 = TexMobject('x^n=', 'x', '\\times', 'x^{n-1}')
        f2.next_to(t3, RIGHT)
        dg = VGroup(t3, f2).center().shift(
            UP)  # Place t3 so the final result is centered
        f1.next_to(t3, RIGHT)
        b1 = BraceLabel(f1[1:-1],
                        '$n$ times',
                        brace_direction=UP,
                        label_constructor=TextMobject)
        g = VGroup(t3, f1[:-1], b1)  # For display
        self.play(
            FadeOut(t1),
            FadeOut(t2),
            FadeInFromDown(g),
        )
        self.wait(duration=3)

        b2 = BraceLabel(f1[3],
                        '$n-1$ times',
                        brace_direction=UP,
                        label_constructor=TextMobject)
        self.play(ReplacementTransform(b1, b2), )
        self.wait(duration=2)

        self.play(
            ReplacementTransform(f1[3], f2[3]),
            FadeOut(b2),
        )
        self.wait(duration=2)

        t4 = TextMobject("That's starting to feel a bit recursive!")
        t4.next_to(dg, DOWN, buff=MED_LARGE_BUFF)
        self.play(FadeIn(t4))
        self.wait()

        code_scale = 0.75
        power_code = CodeBlock(
            'Java',
            r"""
            public static int power(int x, int n) {
                if (n == 0) {
                    return 1;
                }
                int t = power(x, n - 1);
                return x * t;
            }
            """,
            code_scale=code_scale,
        )
        self.play(
            *[FadeOut(o) for o in [t3, t4, f1[:-1], f2[3]]],
            FadeInFromDown(power_code),
        )
        self.wait()

        b1 = BraceLabel(
            power_code.get_code().get_lines((2, 4)),
            'Remember $x^0=1$, because math!',
            brace_direction=RIGHT,
            label_constructor=TextMobject,
            label_scale=0.75,
        )
        self.play(ShowCreation(b1))
        self.wait(duration=2)

        t1 = TextMobject("Let's step through it to see how it goes...")
        t1.next_to(power_code, DOWN)
        self.play(FadeIn(t1), )
        self.wait()

        # Start stepping through this and see it go.
        main_code = CodeBlock(
            'Java',
            r"""
            public static void main(String[] args) {
                int y = power(4, 3);
            }
            """,
            line_offset=7,
            code_scale=code_scale - 0.1,
        )
        frame_width = 3.5
        main_frame = StackFrame(main_code,
                                'main()',
                                9, ['y'],
                                width=frame_width)
        main_code.highlight_lines(9)
        VGroup(main_code, main_frame).arrange(RIGHT,
                                              buff=LARGE_BUFF).to_edge(DOWN)
        self.play(
            FadeOut(t1),
            FadeOut(b1),
            power_code.to_edge,
            UP,
            FadeInFromDown(main_frame),
            FadeInFromDown(main_code),
        )
        self.wait()

        def call_power(x, n, call_stack):
            stack_frame = StackFrame(power_code,
                                     'power(%d, %d)' % (x, n),
                                     1, ['x', 'n', 't'],
                                     width=frame_width)
            call_stack.animate_call(stack_frame, self)

            self.play(*stack_frame.get_update_line_anims(2),
                      stack_frame.update_slot, 'x', x, stack_frame.update_slot,
                      'n', n)

            if n == 0:
                self.play(*stack_frame.get_update_line_anims(3))
                self.wait()
                call_stack.animate_return(self)
                return 1
            else:
                self.play(*stack_frame.get_update_line_anims(5))
                self.wait()
                t = call_power(x, n - 1, call_stack)

                self.play(
                    *stack_frame.get_update_line_anims(6),
                    stack_frame.update_slot,
                    't',
                    t,
                )
                self.wait()
                call_stack[-1].code = power_code
                call_stack.animate_return(self)
                return x * t

        result = call_power(4, 3, CallStack(main_frame))
        self.play(*main_frame.get_update_line_anims(10),
                  main_frame.update_slot, 'y', result)
        self.wait()

        final_eq = TexMobject('4^3=', '64')
        final_eq.next_to(power_code, DOWN, buff=LARGE_BUFF)
        lhs = final_eq[0].copy().move_to(
            main_code.get_code().get_lines(2)[12:15])
        rhs = final_eq[1].copy().move_to(main_frame.slots()[0])
        self.play(
            ReplacementTransform(lhs, final_eq[0]),
            ReplacementTransform(rhs, final_eq[1]),
        )
        self.wait(duration=2)

        self.play(*[FadeOut(o) for o in [final_eq, main_frame, main_code]])
        self.wait()
Example #3
0
    def construct(self):
        title = TextMobject('The Call Stack').to_edge(UP)
        self.add(title)

        frame_width = 3.0
        dummy_code = CodeBlock('Java', 'foo')
        args_ref = TextMobject('[ ]').scale(0.5)
        main_frame = StackFrame(dummy_code,
                                'main()',
                                3, [('args', args_ref)],
                                width=frame_width)
        foo_frame = StackFrame(dummy_code,
                               'foo()',
                               6, [('n', 6)],
                               width=frame_width)
        bar_frame = StackFrame(dummy_code,
                               'bar(1, 2)',
                               13, [('x', 1), ('y', 2), ('a', 3), ('b', 6)],
                               width=frame_width)
        main_frame.to_edge(DOWN)
        foo_frame.next_to(main_frame, UP)
        bar_frame.next_to(foo_frame, UP)
        frame_group = VGroup(main_frame, foo_frame, bar_frame)
        self.play(
            LaggedStartMap(FadeInFrom,
                           frame_group,
                           group=frame_group,
                           direction=UP,
                           lag_ratio=0.5))
        self.wait()

        text_scale = 0.75
        b1 = BraceLabel(main_frame,
                        'Always starts\\\\with main()',
                        brace_direction=LEFT,
                        label_constructor=TextMobject,
                        label_scale=text_scale)
        # b2 = BraceLabel(foo_frame, ['Calls push frames,\\\\', 'returns pop'],
        #                 brace_direction=RIGHT, label_constructor=TextMobject,
        #                 alignment='')
        b3 = BraceLabel(bar_frame.slots()[0:2],
                        'Parameters',
                        brace_direction=RIGHT,
                        label_constructor=TextMobject,
                        label_scale=text_scale)
        b4 = BraceLabel(bar_frame.slots()[2:4],
                        'Locals',
                        brace_direction=RIGHT,
                        label_constructor=TextMobject,
                        label_scale=text_scale)
        b5 = BraceLabel(bar_frame.slots()[0:4],
                        'Storage for all\\\\variables in a function',
                        brace_direction=LEFT,
                        label_constructor=TextMobject,
                        label_scale=text_scale)

        notes = VGroup(b1, b5, b3, b4)

        box_count = 8
        colors = color_gradient([BLUE, ORANGE], box_count)
        little_boxes = VGroup(*[
            Rectangle(height=0.25, width=0.75, fill_opacity=1, color=colors[i])
            for i in range(box_count)
        ])
        little_boxes.arrange(UP, buff=0.1)

        push_up = TextMobject('Calls push\\\\frames').scale(text_scale)
        push_up.next_to(little_boxes, DOWN)
        pua = Arrow(push_up.get_bottom(), push_up.get_top()).scale(2)
        pua.next_to(push_up, LEFT)
        pug = VGroup(push_up, pua)

        pop_down = TextMobject('Returns pop\\\\frames').scale(text_scale)
        pop_down.next_to(little_boxes, UP)
        pda = Arrow(pop_down.get_top(), pop_down.get_bottom()).scale(2)
        pda.next_to(pop_down, RIGHT)
        pdg = VGroup(pop_down, pda)

        bg = VGroup(little_boxes, pug, pdg)
        bg.to_edge(RIGHT, buff=MED_LARGE_BUFF)

        self.play(
            LaggedStartMap(ShowCreation,
                           notes,
                           group=notes,
                           lag_ratio=0.7,
                           run_time=3.0))
        self.play(
            FadeIn(pug),
            LaggedStartMap(FadeInFrom,
                           little_boxes,
                           lambda m: (m, RIGHT),
                           lag_ratio=1.0,
                           run_time=3.0))
        self.play(FadeIn(pdg))
        pdg.generate_target()
        pdg.target.next_to(little_boxes[3], UP,
                           submobject_to_align=pdg[0]).set_opacity(1.0)
        self.play(
            MoveToTarget(pdg, run_time=3.0),
            LaggedStartMap(FadeOutAndShift,
                           VGroup(*list(reversed(little_boxes))[:4]),
                           lambda m: (m, RIGHT),
                           lag_ratio=1.0,
                           run_time=2.0),
        )
        self.wait(duration=5)

        end_scale_group = VGroup(*self.mobjects)
        end_fade_group = VGroup(title)
        self.animate_yt_end_screen(end_scale_group,
                                   end_fade_group,
                                   show_elements=False,
                                   show_rects=False)