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()
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()
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)