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
def construct(self): frame_width = 3 f1 = StackFrame('foo()', 1, ['a', 'b', ('c', 2)], width=frame_width) self.play(FadeIn(f1)) self.play(f1.set_line, 2) self.play(f1.set_line, 3) self.play(f1.update_slot, 'b', 42) self.play(f1.set_line, 4, f1.update_slot, 'a', 43) f2 = StackFrame('bar(1, 2, 3)', 1, ['x'], width=frame_width) f2.next_to(f1, DOWN, buff=SMALL_BUFF) self.play(FadeIn(f2)) self.wait() self.play(FadeOut(f1), FadeOut(f2)) self.wait()
def call_power2(x, n, call_stack, cc_num): new_cc_num = TextMobject(str(len(call_stack) - 1)).move_to(cc_num).set_color(YELLOW) update_cc_anims = [ReplacementTransform(cc_num, new_cc_num)] call_ret_delay = 0.5 stack_frame = StackFrame(power2_code, 'power(%d, %d)' % (x, n), 1, ['x', 'n', 't'], width=frame_width) call_stack.animate_call(stack_frame, self, extra_anims=update_cc_anims) 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(duration=call_ret_delay) call_stack.animate_return(self) return 1 else: self.play(*stack_frame.get_update_line_anims(5)) self.wait(duration=call_ret_delay) t = call_power2(x, n // 2, call_stack, new_cc_num) self.play( *stack_frame.get_update_line_anims(6), stack_frame.update_slot, 't', t, ) if n % 2 == 0: self.play(*stack_frame.get_update_line_anims(7)) self.wait(duration=call_ret_delay) call_stack.animate_return(self) return t * t self.play(*stack_frame.get_update_line_anims(9)) self.wait(duration=call_ret_delay) call_stack.animate_return(self) return t * t * x
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
def call_fib(run_ctx, call_stack, current_node, n): run_ctx['call_count'] += 1 call_ret_delay = 0.5 stack_frame = StackFrame(fib_code, 'fib(%d)' % n, 1, ['n', 'fn1', 'fn2'], width=call_stack[0].width, text_scale=call_stack[0].text_scale) sr = SurroundingRectangle(current_node) extra_anims = [current_node.set_color, YELLOW] if tree.current_rect: extra_anims.append(ReplacementTransform(tree.current_rect, sr)) else: extra_anims.append(ShowCreation(sr)) tree.current_rect = sr if current_node.parent: extra_anims.append( ApplyMethod(current_node.parent.set_color, ORANGE)) call_stack.animate_call(stack_frame, self, extra_anims=extra_anims) self.play( *stack_frame.get_update_line_anims(2), stack_frame.update_slot, 'n', n, ) if n == 0: self.play(*stack_frame.get_update_line_anims(3)) self.wait(duration=call_ret_delay) extra_anims = [] if run_ctx['call_count'] == 5: s1 = TextMobject("and we know $F_0=0$") s1.move_to(tree_area_frame).shift(DOWN) self.play(FadeIn(s1)) extra_anims.append(FadeOut(s1)) extra_anims.extend(update_for_return(current_node, 0, run_ctx)) call_stack.animate_return(self, extra_anims=extra_anims) return 0 self.play(*stack_frame.get_update_line_anims(5)) if n == 1: self.play(*stack_frame.get_update_line_anims(6)) self.wait(duration=call_ret_delay) extra_anims = [] if run_ctx['call_count'] == 4: s1 = TextMobject("we know $F_1=1$") s1.move_to(tree_area_frame).shift(DOWN) self.play(FadeIn(s1)) extra_anims.append(FadeOut(s1)) extra_anims.extend(update_for_return(current_node, 1, run_ctx)) call_stack.animate_return(self, extra_anims=extra_anims) return 1 self.play(*stack_frame.get_update_line_anims(8)) self.wait(duration=call_ret_delay) s1 = None if run_ctx['call_count'] == 1: s1 = TextMobject('we need $F_3$ and $F_2$') s1.move_to(tree_area_frame) self.play(FadeIn(s1)) self.wait() elif run_ctx['call_count'] == 2: s1 = TextMobject('and now we need $F_2$ and $F_1$') s1.move_to(tree_area_frame).shift(DOWN) self.play(FadeIn(s1)) self.wait() left_node, ll = tree.add_left_node(current_node, '$F_%d$' % (n - 1)) right_node, rl = tree.add_right_node(current_node, '$F_%d$' % (n - 2)) left_node.set_color(BLUE) right_node.set_color(BLUE) self.add(ll, rl, tree.current_rect) # Keep the rect on top of lines self.play(*[FadeIn(o) for o in [left_node, ll, right_node, rl]]) if s1: self.wait() self.play(FadeOut(s1)) s1 = None fn1 = call_fib(run_ctx, call_stack, left_node, n - 1) if run_ctx['call_count'] == 4: s1 = TextMobject("now do the other half") s1.move_to(tree_area_frame).shift(DOWN) self.play(FadeIn(s1)) elif run_ctx['call_count'] == 6: t1 = TextMobject("Using a ``tree'' for this is \\textit{much} " "more helpful!").scale(0.8) t2 = TextMobject("Shows where we've ", "been", ", where we ", "are", ",\\\\and where we're ", "going") t2.scale(0.8) t2.set_color_by_tex_to_color_map({ "been": GREEN_SCREEN, "are": YELLOW, "going": BLUE, }) t1.move_to(tree_area_frame).shift(DOWN) t2.next_to(t1, DOWN, buff=MED_SMALL_BUFF) self.play(FadeIn(t1)) self.wait() self.play(Write(t2)) self.wait() run_ctx['final_desc'] = VGroup(t1, t2) self.play( *stack_frame.get_update_line_anims(9), stack_frame.update_slot, 'fn1', fn1, ) if s1: self.wait() self.play(FadeOut(s1)) s1 = None self.wait(duration=call_ret_delay) fn2 = call_fib(run_ctx, call_stack, right_node, n - 2) extra_anims = [] if run_ctx['call_count'] == 5: s1 = TextMobject("add the two halves") s1.move_to(tree_area_frame).shift(DOWN) self.play(FadeIn(s1)) extra_anims.append(FadeOut(s1)) run_ctx['update_node_delay'] = 0.1 self.play( *stack_frame.get_update_line_anims(10), stack_frame.update_slot, 'fn2', fn2, ) self.wait(duration=call_ret_delay) extra_anims.extend( update_for_return(current_node, fn1 + fn2, run_ctx)) call_stack.animate_return(self, extra_anims=extra_anims) return fn1 + fn2
def construct(self): fib_code = self.get_fib_code(0.75) fib_code.to_edge(UP) self.add(fib_code) fc2 = self.get_fib_code(0.6) fc2.to_edge(UR) self.play(ReplacementTransform(fib_code, fc2)) fib_code = fc2 first_fib_arg = 4 main_code = CodeBlock( 'Java', r""" public static void main(String[] args) { int x = fib(%d); } """ % first_fib_arg, line_offset=11, code_scale=0.6, ) main_frame = StackFrame(main_code, 'main()', 13, ['x'], width=2.5, text_scale=0.6) main_code.highlight_lines(13) VGroup(main_code, main_frame).arrange(RIGHT, buff=LARGE_BUFF * 2).to_edge(DOWN) tree_area_frame = Polygon( np.array((-FRAME_X_RADIUS, FRAME_Y_RADIUS, 0)), np.array((fib_code.get_corner(UL)[0], FRAME_Y_RADIUS, 0)), np.array((fib_code.get_corner(DL)[0], main_code.get_top()[1], 0)), np.array((-FRAME_X_RADIUS, main_code.get_top()[1], 0)), mark_paths_closed=True, close_new_points=True, ) ready_desc = TextMobject( "We'll compute $F_%d$\\\\and track \\textit{all} " "our work as we go" % first_fib_arg) ready_desc.move_to(tree_area_frame) self.play(FadeIn(ready_desc)) self.wait(duration=2) self.play( fib_code.to_edge, UP, FadeInFromDown(main_frame), FadeInFromDown(main_code), ) tree_text_scale = 1.0 tmp_leaf = TextMobject('$F_%d$' % 1).scale(tree_text_scale) leaf_width = tmp_leaf.get_width() level_spread_factors = [4, 1.9, 1.75] level_spreads = [f * leaf_width for f in level_spread_factors] tree = BinaryTree( max_depth=first_fib_arg, leaf_height=tmp_leaf.get_height(), level_buff=LARGE_BUFF * 1.5, level_spreads=level_spreads, text_scale=tree_text_scale, ) def update_for_return(current_node, val, run_ctx): v = TextMobject(str(val)) # v = TextMobject(current_node.label.tex_string, '$=%d$' % val) v.set_color(GREEN_SCREEN) v.scale(tree_text_scale) v.move_to(current_node.label) self.play(ReplacementTransform(current_node.label, v)) self.wait(duration=run_ctx['update_node_delay']) extra_anims = [] if current_node.parent: sr = SurroundingRectangle(current_node.parent) extra_anims.append(ReplacementTransform(tree.current_rect, sr)) tree.current_rect = sr extra_anims.append( ApplyMethod(current_node.parent.set_color, YELLOW)) else: extra_anims.append(FadeOut(tree.current_rect)) return extra_anims def call_fib(run_ctx, call_stack, current_node, n): run_ctx['call_count'] += 1 call_ret_delay = 0.5 stack_frame = StackFrame(fib_code, 'fib(%d)' % n, 1, ['n', 'fn1', 'fn2'], width=call_stack[0].width, text_scale=call_stack[0].text_scale) sr = SurroundingRectangle(current_node) extra_anims = [current_node.set_color, YELLOW] if tree.current_rect: extra_anims.append(ReplacementTransform(tree.current_rect, sr)) else: extra_anims.append(ShowCreation(sr)) tree.current_rect = sr if current_node.parent: extra_anims.append( ApplyMethod(current_node.parent.set_color, ORANGE)) call_stack.animate_call(stack_frame, self, extra_anims=extra_anims) self.play( *stack_frame.get_update_line_anims(2), stack_frame.update_slot, 'n', n, ) if n == 0: self.play(*stack_frame.get_update_line_anims(3)) self.wait(duration=call_ret_delay) extra_anims = [] if run_ctx['call_count'] == 5: s1 = TextMobject("and we know $F_0=0$") s1.move_to(tree_area_frame).shift(DOWN) self.play(FadeIn(s1)) extra_anims.append(FadeOut(s1)) extra_anims.extend(update_for_return(current_node, 0, run_ctx)) call_stack.animate_return(self, extra_anims=extra_anims) return 0 self.play(*stack_frame.get_update_line_anims(5)) if n == 1: self.play(*stack_frame.get_update_line_anims(6)) self.wait(duration=call_ret_delay) extra_anims = [] if run_ctx['call_count'] == 4: s1 = TextMobject("we know $F_1=1$") s1.move_to(tree_area_frame).shift(DOWN) self.play(FadeIn(s1)) extra_anims.append(FadeOut(s1)) extra_anims.extend(update_for_return(current_node, 1, run_ctx)) call_stack.animate_return(self, extra_anims=extra_anims) return 1 self.play(*stack_frame.get_update_line_anims(8)) self.wait(duration=call_ret_delay) s1 = None if run_ctx['call_count'] == 1: s1 = TextMobject('we need $F_3$ and $F_2$') s1.move_to(tree_area_frame) self.play(FadeIn(s1)) self.wait() elif run_ctx['call_count'] == 2: s1 = TextMobject('and now we need $F_2$ and $F_1$') s1.move_to(tree_area_frame).shift(DOWN) self.play(FadeIn(s1)) self.wait() left_node, ll = tree.add_left_node(current_node, '$F_%d$' % (n - 1)) right_node, rl = tree.add_right_node(current_node, '$F_%d$' % (n - 2)) left_node.set_color(BLUE) right_node.set_color(BLUE) self.add(ll, rl, tree.current_rect) # Keep the rect on top of lines self.play(*[FadeIn(o) for o in [left_node, ll, right_node, rl]]) if s1: self.wait() self.play(FadeOut(s1)) s1 = None fn1 = call_fib(run_ctx, call_stack, left_node, n - 1) if run_ctx['call_count'] == 4: s1 = TextMobject("now do the other half") s1.move_to(tree_area_frame).shift(DOWN) self.play(FadeIn(s1)) elif run_ctx['call_count'] == 6: t1 = TextMobject("Using a ``tree'' for this is \\textit{much} " "more helpful!").scale(0.8) t2 = TextMobject("Shows where we've ", "been", ", where we ", "are", ",\\\\and where we're ", "going") t2.scale(0.8) t2.set_color_by_tex_to_color_map({ "been": GREEN_SCREEN, "are": YELLOW, "going": BLUE, }) t1.move_to(tree_area_frame).shift(DOWN) t2.next_to(t1, DOWN, buff=MED_SMALL_BUFF) self.play(FadeIn(t1)) self.wait() self.play(Write(t2)) self.wait() run_ctx['final_desc'] = VGroup(t1, t2) self.play( *stack_frame.get_update_line_anims(9), stack_frame.update_slot, 'fn1', fn1, ) if s1: self.wait() self.play(FadeOut(s1)) s1 = None self.wait(duration=call_ret_delay) fn2 = call_fib(run_ctx, call_stack, right_node, n - 2) extra_anims = [] if run_ctx['call_count'] == 5: s1 = TextMobject("add the two halves") s1.move_to(tree_area_frame).shift(DOWN) self.play(FadeIn(s1)) extra_anims.append(FadeOut(s1)) run_ctx['update_node_delay'] = 0.1 self.play( *stack_frame.get_update_line_anims(10), stack_frame.update_slot, 'fn2', fn2, ) self.wait(duration=call_ret_delay) extra_anims.extend( update_for_return(current_node, fn1 + fn2, run_ctx)) call_stack.animate_return(self, extra_anims=extra_anims) return fn1 + fn2 root_node, _ = tree.add_left_node(None, '$F_%d$' % first_fib_arg) root_node.move_to(tree_area_frame) root_node.to_edge(UP) self.play(ShowCreation(root_node)) self.wait() self.play(FadeOut(ready_desc)) run_ctx = { 'call_count': 0, 'update_node_delay': 1.0, } result = call_fib(run_ctx, CallStack(main_frame, scroll_height=1), root_node, first_fib_arg) self.play( *main_frame.get_update_line_anims(14), main_frame.update_slot, 'x', result, ) self.wait(duration=2) self.play(FadeOutAndShiftDown(main_code), FadeOutAndShiftDown(main_frame)) self.wait() t1 = TextMobject("It is common to see recursive algorithms " "described with trees") t2 = TextMobject("\\textit{Try drawing them out as you go!}") t2.set_color(GREEN) t2.to_edge(DOWN) t1.next_to(t2, UP, buff=MED_LARGE_BUFF) self.play(FadeInFromDown(t1)) self.wait(duration=1.5) self.play(Write(t2)) self.wait(duration=5) x = self.camera.get_mobjects_to_display(self.mobjects) end_scale_group = VGroup(*x) end_fade_group = VGroup() self.animate_yt_end_screen(end_scale_group, end_fade_group, end_scale=0.6, show_elements=False)
def call_fib(run_ctx, n, call_stack): run_ctx['call_count'] += 1 call_ret_delay = 0.5 stack_frame = StackFrame(fib_code, 'fib(%d)' % n, 1, ['n', 'fn1', 'fn2'], width=frame_width) call_stack.animate_call(stack_frame, self) self.play( *stack_frame.get_update_line_anims(2), stack_frame.update_slot, 'n', n, ) if n == 0: self.play(*stack_frame.get_update_line_anims(3)) self.wait(duration=call_ret_delay) call_stack.animate_return(self) return 0 self.play(*stack_frame.get_update_line_anims(5)) if n == 1: self.play(*stack_frame.get_update_line_anims(6)) self.wait(duration=call_ret_delay) call_stack.animate_return(self) return 1 self.play(*stack_frame.get_update_line_anims(8)) self.wait(duration=call_ret_delay) fn1 = call_fib(run_ctx, n - 1, call_stack) self.play( *stack_frame.get_update_line_anims(9), stack_frame.update_slot, 'fn1', fn1, ) if run_ctx['call_count'] == 4: t1 = TextMobject('\\textit{Whoa!!}') t1.set_color(YELLOW).scale(1.2) t1.next_to(fib_code, LEFT, buff=MED_LARGE_BUFF) self.play(GrowFromCenter(t1)) t2 = TextMobject("\\textit{I'm lost...}") t2.next_to(t1, DOWN, buff=LARGE_BUFF) self.play(FadeInFromPoint(t2, t2.get_center())) run_ctx['whoa_desc'] = VGroup(t1, t2) self.wait(duration=call_ret_delay) fn2 = call_fib(run_ctx, n - 2, call_stack) self.play( *stack_frame.get_update_line_anims(10), stack_frame.update_slot, 'fn2', fn2, ) self.wait(duration=call_ret_delay) call_stack.animate_return(self) return fn1 + fn2
def construct(self): fib_code = CodeBlock( 'Java', r""" public static int fib(int n) { if (n == 0) { return 0; } if (n == 1) { return 1; } int fn1 = fib(n - 1); int fn2 = fib(n - 2); return fn1 + fn2; } """, ) self.add(fib_code) main_code = CodeBlock( 'Java', r""" public static void main(String[] args) { int x = fib(3); } """, line_offset=11, code_scale=0.6, ) frame_width = 2.5 main_frame = StackFrame(main_code, 'main()', 13, ['x'], width=frame_width) main_code.highlight_lines(13) VGroup(main_code, main_frame).arrange(RIGHT, buff=LARGE_BUFF * 2).to_edge(DOWN) self.play( fib_code.to_edge, UP, FadeInFromDown(main_frame), FadeInFromDown(main_code), ) self.wait() def call_fib(run_ctx, n, call_stack): run_ctx['call_count'] += 1 call_ret_delay = 0.5 stack_frame = StackFrame(fib_code, 'fib(%d)' % n, 1, ['n', 'fn1', 'fn2'], width=frame_width) call_stack.animate_call(stack_frame, self) self.play( *stack_frame.get_update_line_anims(2), stack_frame.update_slot, 'n', n, ) if n == 0: self.play(*stack_frame.get_update_line_anims(3)) self.wait(duration=call_ret_delay) call_stack.animate_return(self) return 0 self.play(*stack_frame.get_update_line_anims(5)) if n == 1: self.play(*stack_frame.get_update_line_anims(6)) self.wait(duration=call_ret_delay) call_stack.animate_return(self) return 1 self.play(*stack_frame.get_update_line_anims(8)) self.wait(duration=call_ret_delay) fn1 = call_fib(run_ctx, n - 1, call_stack) self.play( *stack_frame.get_update_line_anims(9), stack_frame.update_slot, 'fn1', fn1, ) if run_ctx['call_count'] == 4: t1 = TextMobject('\\textit{Whoa!!}') t1.set_color(YELLOW).scale(1.2) t1.next_to(fib_code, LEFT, buff=MED_LARGE_BUFF) self.play(GrowFromCenter(t1)) t2 = TextMobject("\\textit{I'm lost...}") t2.next_to(t1, DOWN, buff=LARGE_BUFF) self.play(FadeInFromPoint(t2, t2.get_center())) run_ctx['whoa_desc'] = VGroup(t1, t2) self.wait(duration=call_ret_delay) fn2 = call_fib(run_ctx, n - 2, call_stack) self.play( *stack_frame.get_update_line_anims(10), stack_frame.update_slot, 'fn2', fn2, ) self.wait(duration=call_ret_delay) call_stack.animate_return(self) return fn1 + fn2 run_ctx = { 'call_count': 0, } result = call_fib(run_ctx, 3, CallStack(main_frame)) self.play( *main_frame.get_update_line_anims(14), main_frame.update_slot, 'x', result, ) self.wait(duration=2) hrm_desc = TextMobject( "That worked, but it was really hard to follow!") hrm_desc.next_to(fib_code, DOWN, buff=MED_LARGE_BUFF) self.play(FadeOutAndShiftDown(main_frame), FadeOutAndShiftDown(main_code), FadeOut(run_ctx['whoa_desc'])) self.play(FadeInFromDown(hrm_desc)) self.wait(duration=2.5) t1 = TextMobject("The call stack shows where we are at any moment") t1.move_to(hrm_desc) t2 = TextMobject("but we don't know where we've been...") t2.next_to(t1, DOWN) t3 = TextMobject("or where we're going...") t3.next_to(t2, DOWN) self.play(ReplacementTransform(hrm_desc, t1)) self.wait(duration=2) self.play(FadeInFromDown(t2)) self.wait(duration=2) self.play(FadeInFromDown(t3)) self.wait(duration=2) t4 = TextMobject( "Let's go again, but draw a new picture along the way") t4.move_to(t1) self.play(*[FadeOutAndShift(o, UP) for o in [t1, t2, t3]], FadeInFromDown(t4)) self.wait(duration=2.5) self.play(FadeOut(t4))
def construct(self): code_scale = 0.75 ss_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, ) t1 = TextMobject( 'Consider this somewhat strange square root function:') t1.next_to(ss_code, UP, buff=LARGE_BUFF) self.play( FadeIn(t1), ShowCreation(ss_code), ) self.wait(duration=2.5) t2 = TextMobject( 'It has a recursive call, meaning \\textit{it calls itself.}') t2.next_to(ss_code, DOWN, buff=LARGE_BUFF) # 012345 6 7 8901234567890 1 2345 # double b = StrangeSqrt(a * -1); rl = ss_code.get_code().get_lines(3) rc_highlight = SurroundingRectangle(rl[8:-1]) self.play( FadeIn(t2), FadeIn(rc_highlight), ) self.wait(duration=3) self.play( FadeOut(t2), FadeOut(rc_highlight), ) # Run with a positive input main_code = CodeBlock( 'Java', r""" public static void main(String[] args) { double n = StrangeSqrt(4); } """, 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) t3 = TextMobject("Let's see what it does with a positive input...")\ .next_to(ss_code, DOWN, buff=LARGE_BUFF) self.play( FadeOut(t1), MaintainPositionRelativeTo(t1, ss_code), ss_code.to_edge, UP, FadeIn(t3), MaintainPositionRelativeTo(t3, ss_code), ) self.wait() self.play( FadeInFromDown(main_frame), FadeInFromDown(main_code), ) self.wait() ss_frame = StackFrame(ss_code, 'StrangeSqrt(4)', 1, ['a', 'b'], width=frame_width) ss_frame.next_to(main_frame, UP, buff=SMALL_BUFF) xi = main_code.pre_call(ss_code, 1) self.play( *main_code.get_control_transfer_counterclockwise(xi), FadeInFrom(ss_frame, UP), FadeOut(t3), ) ss_code.post_control_transfer(xi, self) self.wait() self.play( ss_code.highlight_lines, 2, ss_frame.set_line, 2, ss_frame.update_slot, 'a', 4, ) self.wait() self.play( ss_code.highlight_lines, 6, ss_frame.set_line, 6, ) self.wait() xi = ss_code.pre_return(main_code, 9) self.play( *ss_code.get_control_transfer_clockwise(xi), Uncreate(ss_frame), ) main_code.post_control_transfer(xi, self) self.wait() t1 = TextMobject('That seems pretty normal...').next_to( ss_code, DOWN, buff=LARGE_BUFF) self.play( main_code.highlight_lines, 10, main_frame.set_line, 10, main_frame.update_slot, 'n', 2, FadeIn(t1), ) self.wait(duration=2) t2 = TextMobject("Let's try it with a negative input...")\ .next_to(ss_code, DOWN, buff=LARGE_BUFF) self.play( FadeOutAndShiftDown(main_code), FadeOutAndShiftDown(main_frame), FadeOut(t1), FadeIn(t2), ) # Run with a negative input and see the recursion main_code = CodeBlock( 'Java', r""" public static void main(String[] args) { double n = StrangeSqrt(-4); } """, line_offset=7, code_scale=code_scale - 0.1, ) 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_code, 'StrangeSqrt(-4)', 1, ['a', 'b'], width=frame_width) ss_frame.next_to(main_frame, UP, buff=SMALL_BUFF) xi = main_code.pre_call(ss_code, 1) self.play( *main_code.get_control_transfer_counterclockwise(xi), FadeInFrom(ss_frame, UP), FadeOut(t2), ) ss_code.post_control_transfer(xi, self) self.wait() self.play( ss_code.highlight_lines, 2, ss_frame.set_line, 2, ss_frame.update_slot, 'a', -4, ) self.wait() self.play( ss_code.highlight_lines, 3, ss_frame.set_line, 3, ) self.wait() self.play(Indicate(rl[8:-1]), ) ss2_frame = StackFrame(ss_code, 'StrangeSqrt(4)', 1, ['a', 'b'], width=frame_width) ss2_frame.next_to(ss_frame, UP, buff=SMALL_BUFF) r1_call_site_rect = ss_code.get_current_highlight().copy().set_color( WHITE) self.play( FadeInFrom(ss2_frame, UP), ss_code.highlight_lines, 1, FadeIn(r1_call_site_rect), ) self.wait() stack = VGroup(main_frame, ss_frame, ss2_frame) t1 = TextMobject('Same function, but with\\\\a new frame on the stack')\ .next_to(stack, LEFT, buff=LARGE_BUFF).shift(UP * 0.5) a = Arrow(t1.get_right(), ss2_frame.get_left(), stroke_width=3) self.play( FadeIn(t1), GrowArrow(a), ) self.wait(duration=2) # Now we have a positive input again self.play( ss_code.highlight_lines, 2, ss2_frame.set_line, 2, ss2_frame.update_slot, 'a', 4, ) self.wait() self.play( ss_code.highlight_lines, 6, ss2_frame.set_line, 6, FadeOut(t1), FadeOut(a), ) self.wait() t1 = TextMobject('The stack tells us where to return,\\\\and with which values')\ .next_to(stack, LEFT, buff=LARGE_BUFF).shift(UP * 0.5) a = Arrow(t1.get_right(), ss_frame.get_left(), stroke_width=3) self.play( FadeIn(t1), GrowArrow(a), ) self.wait(duration=2) # Return back to line 3... self.play( FadeOut(r1_call_site_rect), Uncreate(ss2_frame), ss_code.highlight_lines, 3, ss_frame.set_line, 3, ) self.wait() self.play( ss_code.highlight_lines, 4, ss_frame.set_line, 4, ss_frame.update_slot, 'b', 2, FadeOut(t1), FadeOut(a), ) self.wait() xi = ss_code.pre_return(main_code, 9) self.play( *ss_code.get_control_transfer_clockwise(xi), Uncreate(ss_frame), ) main_code.post_control_transfer(xi, self) self.wait() self.play( main_code.highlight_lines, 10, main_frame.set_line, 10, main_frame.update_slot, 'n', 2, ) self.wait() t1 = TextMobject( 'Thinking about the call stack can help you keep track of\\\\' 'what a recursive function is doing at any given moment.').next_to( ss_code, DOWN, buff=LARGE_BUFF) self.play( FadeOutAndShiftDown(main_code), FadeOutAndShiftDown(main_frame), FadeIn(t1), ) self.wait(duration=3) t2 = TextMobject('Try writing down part of the stack as you go.')\ .next_to(t2, DOWN, buff=LARGE_BUFF) self.play(FadeInFromDown(t2)) self.wait(duration=2) self.play( FadeOut(t1), FadeOut(t2), ) 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): # - 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): code_scale = 0.7 power2_code = CodeBlock( 'Java', r""" public static int power2(int x, int n) { if (n == 0) { return 1; } int t = power2(x, n / 2); if (n % 2 == 0) { return t * t; } return t * t * x; } """, code_scale=code_scale, ) power2_code.to_edge(UP) self.add(power2_code) t1 = TextMobject( "This version should call itself fewer than $n$ times") t1.next_to(power2_code, DOWN, buff=LARGE_BUFF) self.play(FadeInFromDown(t1)) self.wait(duration=2.5) t2 = TextMobject( 'How many times do you think \\texttt{power2(2,30)} will call itself?', tex_to_color_map={'30': YELLOW}) t2.next_to(t1, ORIGIN) self.play(FadeOutAndShift(t1, UP), FadeInFromDown(t2)) self.wait(duration=2) t3 = TextMobject("We know it'll be less than 30! Maybe 15?") t3.next_to(t2, DOWN, buff=MED_LARGE_BUFF) self.play(FadeInFromDown(t3)) self.wait(duration=2) t4 = TextMobject("How about a lot less? Let's see...") t4.next_to(t3, ORIGIN) self.play(ReplacementTransform(t3, t4)) self.wait(duration=2) self.play(*[FadeOut(o) for o in self.mobjects if o != power2_code]) # Start stepping through this and see it go. main_code = CodeBlock( 'Java', r""" public static void main(String[] args) { int y = power2(2, 30); } """, line_offset=10, code_scale=code_scale - 0.1, ) frame_width = 3.5 main_frame = StackFrame(main_code, 'main()', 12, ['y'], width=frame_width, slot_char_width=8) main_code.highlight_lines(12) VGroup(main_code, main_frame).arrange(RIGHT, buff=LARGE_BUFF).to_edge(DOWN) self.play( FadeInFromDown(main_frame), FadeInFromDown(main_code), ) self.wait() def call_power2(x, n, call_stack, cc_num): new_cc_num = TextMobject(str(len(call_stack) - 1)).move_to(cc_num).set_color(YELLOW) update_cc_anims = [ReplacementTransform(cc_num, new_cc_num)] call_ret_delay = 0.5 stack_frame = StackFrame(power2_code, 'power(%d, %d)' % (x, n), 1, ['x', 'n', 't'], width=frame_width) call_stack.animate_call(stack_frame, self, extra_anims=update_cc_anims) 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(duration=call_ret_delay) call_stack.animate_return(self) return 1 else: self.play(*stack_frame.get_update_line_anims(5)) self.wait(duration=call_ret_delay) t = call_power2(x, n // 2, call_stack, new_cc_num) self.play( *stack_frame.get_update_line_anims(6), stack_frame.update_slot, 't', t, ) if n % 2 == 0: self.play(*stack_frame.get_update_line_anims(7)) self.wait(duration=call_ret_delay) call_stack.animate_return(self) return t * t self.play(*stack_frame.get_update_line_anims(9)) self.wait(duration=call_ret_delay) call_stack.animate_return(self) return t * t * x cc_label = TextMobject('Recursive Calls').scale(0.75) cc_label.to_edge(UL).shift(DOWN) call_counter = TextMobject('0').set_color(YELLOW).next_to( cc_label, DOWN) self.play(FadeIn(call_counter), FadeIn(cc_label)) result = call_power2(2, 30, CallStack(main_frame), call_counter) self.play( *main_frame.get_update_line_anims(13), main_frame.update_slot, 'y', result, ) self.wait() t1 = TextMobject( 'We got our answer in just 5 recursive calls!').set_color(YELLOW) t1.next_to(power2_code, DOWN, buff=MED_LARGE_BUFF) self.play(ShowCreation(t1)) self.wait(duration=2) t1.generate_target() t1.target.center().to_edge(TOP) t2 = TextMobject('But why?').next_to(t1.target, DOWN, buff=LARGE_BUFF) self.play( MoveToTarget(t1), FadeInFromDown(t2), *[FadeOut(o) for o in self.mobjects if o != t1], )
def construct(self): title = TextMobject('Quick Review: The Call Stack').to_edge(UP) code_scale = 0.75 main_code = CodeBlock( 'Java', r""" public static void main(String[] args) { int n = foo(2); } """, code_scale=code_scale, ) foo_code = CodeBlock( 'Java', r""" static int foo(int x) { int n = bar(x + 1, x * 2); return n; } """, code_scale=code_scale, ) bar_code = CodeBlock( 'Java', r""" static int bar(int x, int y) { int a = x + y; return a; } """, code_scale=code_scale, ) fbg = VGroup(main_code, foo_code, bar_code) fbg.arrange(DOWN, buff=MED_SMALL_BUFF, aligned_edge=LEFT) fbg.to_edge(DR) self.play( ShowCreation(title), FadeInFrom(fbg, RIGHT), ) self.wait() frame_width = 3.0 args_ref = TextMobject('[ ]').scale(0.5) main_frame = StackFrame(main_code, 'main()', 3, [('args', args_ref), 'n'], width=frame_width) main_frame.next_to(fbg, LEFT, buff=LARGE_BUFF).to_edge(DOWN) main_code.move_highlight_rect(3) text_scale = 0.75 b1 = BraceLabel(main_frame, 'The call stack\\\\starts with main()', brace_direction=LEFT, label_constructor=TextMobject, label_scale=text_scale) self.play( FadeInFrom(main_frame, UP), main_code.highlight_lines, 3, FadeInFrom(b1, UP), ) self.wait() foo_frame = StackFrame(foo_code, 'foo(2)', 5, ['x', 'n'], width=frame_width) foo_frame.next_to(main_frame, UP, buff=SMALL_BUFF) b2 = BraceLabel(foo_frame, 'Calls push frames', brace_direction=LEFT, label_constructor=TextMobject, label_scale=text_scale) hr_caller, hr_callee = main_code.setup_for_call(foo_code, 1) self.play( main_code.highlight_caller, ReplacementTransform(hr_caller, hr_callee, path_arc=np.pi), FadeInFrom(foo_frame, UP), FadeInFrom(b2, UP), ) foo_code.complete_callee(hr_callee, self) self.wait() self.play( foo_code.highlight_lines, 2, foo_frame.set_line, 6, foo_frame.update_slot, 'x', 2, ) self.wait() bar_frame = StackFrame(bar_code, 'bar(3, 4)', 10, ['x', 'y', 'a'], width=frame_width) bar_frame.next_to(foo_frame, UP, buff=SMALL_BUFF) b3 = BraceLabel(bar_frame, 'Holds arguments\\\\and locals', brace_direction=LEFT, label_constructor=TextMobject, label_scale=text_scale) hr_caller, hr_callee = foo_code.setup_for_call(bar_code, (1, 2)) self.play( foo_code.highlight_caller, ReplacementTransform(hr_caller, hr_callee, path_arc=np.pi), FadeInFrom(bar_frame, UP), FadeInFrom(b3, UP), ) bar_code.complete_callee(hr_callee, self) self.wait() self.play( bar_code.highlight_lines, 3, bar_frame.set_line, 11, bar_frame.update_slot, 'x', 3, bar_frame.update_slot, 'y', 4, ) self.wait() self.play( bar_code.highlight_lines, 4, bar_frame.set_line, 12, bar_frame.update_slot, 'a', 7, ) self.wait(duration=3) hr_returner, hr_returnee = bar_code.setup_for_return(foo_code) self.play( ReplacementTransform(hr_returner, hr_returnee, path_arc=-np.pi), foo_code.highlight_returnee, Uncreate(bar_frame), FadeOutAndShift(b3, LEFT), FadeOutAndShift(b2, LEFT), FadeOutAndShift(b1, LEFT), ) foo_code.complete_returnee(hr_returnee, self) self.wait() b4 = BraceLabel(foo_frame, 'Returns pop', brace_direction=LEFT, label_constructor=TextMobject, label_scale=text_scale) self.play( foo_code.highlight_lines, 3, foo_frame.set_line, 7, foo_frame.update_slot, 'n', 7, ShowCreation(b4), ) self.wait() hr_returner, hr_returnee = foo_code.setup_for_return(main_code) self.play( ReplacementTransform(hr_returner, hr_returnee, path_arc=-np.pi), main_code.highlight_returnee, Uncreate(foo_frame), FadeOutAndShift(b4, LEFT), ) main_code.complete_returnee(hr_returnee, self) self.wait() self.play( main_code.highlight_lines, 4, main_frame.set_line, 4, main_frame.update_slot, 'n', 7, ) self.wait() hr_returner, hr_returnee = main_code.setup_for_return(main_code) hr_returnee.shift(UP * 4) self.play( ReplacementTransform(hr_returner, hr_returnee, path_arc=-np.pi), Uncreate(main_frame), ) self.remove(hr_returnee) self.wait() t1 = TextMobject('Take a moment to think about\\\\' 'where args and locals live,\\\\' 'and what the call stack looks like.').to_edge(LEFT) t2 = TextMobject('\\textit{It will help later!!}').next_to( t1, DOWN, buff=LARGE_BUFF) self.play(FadeIn(t1)) self.wait() self.play(ShowCreation(t2)) self.wait(duration=2)
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)
def construct(self): title = TextMobject('The Call Stack').to_edge(UP) t1 = TextMobject("Alright, let's do a more complicated one!") self.add(title, t1) self.wait() code_scale = 0.75 main_code = CodeBlock( 'Java', r""" public static void main(String[] args) { System.out.println(foo(1)); } """, code_scale=code_scale, ) # Start line: 5 foo_code = CodeBlock( 'Java', r""" foo(int a) { int b = bar(a, 2); int c = bar(a, b); return c; } """, code_scale=code_scale, ) # Start line: 10 bar_code = CodeBlock( 'Java', r""" int bar(int x, int y) { int a = x + y; int b = a * 2; return b; } """, code_scale=code_scale, ) cg = VGroup(main_code, foo_code, bar_code) cg.arrange(DOWN, aligned_edge=LEFT, buff=MED_SMALL_BUFF) cg.next_to(title, DOWN, buff=MED_SMALL_BUFF).to_edge(RIGHT) self.play( FadeOut(t1), FadeInFrom(cg, RIGHT), ) self.wait() frame_width = 3.0 args_ref = TextMobject('[ ]').scale(0.5) main_frame = StackFrame(main_code, 'main()', 3, [('args', args_ref)], width=frame_width) main_frame.next_to(cg, LEFT, buff=LARGE_BUFF * 2).to_edge(DOWN) main_code.move_hidden_highlight(3) self.play( FadeInFromDown(main_frame), main_code.highlight_lines, 3, ) self.wait() foo_frame = StackFrame(foo_code, 'foo(1)', 5, ['a', 'b', 'c'], width=frame_width) foo_frame.next_to(main_frame, UP, buff=SMALL_BUFF) xi = main_code.pre_call(foo_code, 1) self.play( *main_code.get_control_transfer_counterclockwise(xi), FadeInFrom(foo_frame, UP), ) foo_code.post_control_transfer(xi, self) self.wait() self.play(foo_code.highlight_lines, 2, foo_frame.set_line, 6, foo_frame.update_slot, 'a', 1) self.wait() bar_frame = StackFrame(bar_code, 'bar(1, 2)', 10, ['x', 'y', 'a', 'b'], width=frame_width) bar_frame.next_to(foo_frame, UP, buff=SMALL_BUFF) xi = foo_code.pre_call(bar_code, 1) self.play( *foo_code.get_control_transfer_counterclockwise(xi), FadeInFrom(bar_frame, UP), ) bar_code.post_control_transfer(xi, self) self.wait() self.play(bar_code.highlight_lines, 2, bar_frame.set_line, 11, bar_frame.update_slot, 'x', 1, bar_frame.update_slot, 'y', 2) self.wait() self.play(bar_code.highlight_lines, 3, bar_frame.set_line, 12, bar_frame.update_slot, 'a', 3) self.wait() self.play(bar_code.highlight_lines, 4, bar_frame.set_line, 13, bar_frame.update_slot, 'b', 6) self.wait() xi = bar_code.pre_return(foo_code, 2) self.play( *bar_code.get_control_transfer_clockwise(xi), Uncreate(bar_frame), ) foo_code.post_control_transfer(xi, self) self.wait() self.play(foo_code.highlight_lines, 3, foo_frame.set_line, 7, foo_frame.update_slot, 'b', 6) self.wait() bar_frame = StackFrame(bar_code, 'bar(1, 6)', 10, ['x', 'y', 'a', 'b'], width=frame_width) bar_frame.next_to(foo_frame, UP, buff=SMALL_BUFF) xi = foo_code.pre_call(bar_code, 1) self.play( *foo_code.get_control_transfer_counterclockwise(xi), FadeInFrom(bar_frame, UP), ) bar_code.post_control_transfer(xi, self) self.wait() self.play(bar_code.highlight_lines, 2, bar_frame.set_line, 11, bar_frame.update_slot, 'x', 1, bar_frame.update_slot, 'y', 6) self.wait() self.play(bar_code.highlight_lines, 3, bar_frame.set_line, 12, bar_frame.update_slot, 'a', 7) self.wait() self.play(bar_code.highlight_lines, 4, bar_frame.set_line, 13, bar_frame.update_slot, 'b', 14) self.wait() xi = bar_code.pre_return(foo_code, 3) self.play( *bar_code.get_control_transfer_clockwise(xi), Uncreate(bar_frame), ) foo_code.post_control_transfer(xi, self) self.wait() self.play(foo_code.highlight_lines, 4, foo_frame.set_line, 8, foo_frame.update_slot, 'c', 14) self.wait() xi = foo_code.pre_return(main_code, 3) self.play( *foo_code.get_control_transfer_clockwise(xi), Uncreate(foo_frame), ) main_code.post_control_transfer(xi, self) self.wait() def fake_frame(name): frame_name = TextMobject(name).scale(0.75) br = BackgroundRectangle(frame_name, buff=SMALL_BUFF, fill_opacity=0.15) br.set_fill(color=[ORANGE, BLUE]) return VGroup(frame_name, br) println_funcs = [ 'BufferedWriter.write("14", 0, 2)', 'BufferedWriter(Writer).write("14")', 'PrintStream.writeln("14")', 'PrintStream.println(14)', ] println_frames = [fake_frame(f) for f in reversed(println_funcs)] for n, p in zip(println_frames, [main_frame] + println_frames): n.next_to(p, UP, buff=SMALL_BUFF) self.play(FadeInFrom(n, UP)) self.wait() for f in reversed(println_frames): self.play(Uncreate(f)) # self.wait() self.play(main_code.highlight_lines, 4, main_frame.set_line, 4) off_screen_code = CodeBlock('Java', 'off_screen') off_screen_code.shift(UP * 8) # Offscreen up self.add(off_screen_code) xi = main_code.pre_return(off_screen_code, 1) self.play( *main_code.get_control_transfer_clockwise(xi), Uncreate(main_frame), ) off_screen_code.post_control_transfer(xi, self) self.wait() self.play(FadeOut(main_code), FadeOut(foo_code), FadeOut(bar_code)) self.wait()
def construct(self): high_quality = False # - Foo calls bar passing some args. Bar does something, returns. code_scale = 0.75 main_code = CodeBlock( 'Java', r""" public static void main(String[] args) { foo(); } """, code_scale=code_scale, ) foo_code = CodeBlock( 'Java', r""" static int foo() { int n = bar(1, 2); return n; } """, code_scale=code_scale, ) bar_code = CodeBlock( 'Java', r""" static int bar(int x, int y) { int a = x + y; int b = a * 2; return b; } """, code_scale=code_scale, ) fbg = VGroup(foo_code, bar_code) bar_code.next_to(foo_code, DOWN, aligned_edge=LEFT, buff=LARGE_BUFF) fbg.to_edge(TOP) title = TextMobject('We have two Java functions, foo() and bar()') title.to_edge(UP) self.play( ShowCreation(title), FadeInFromDown(fbg), ) self.wait(duration=2) # Let's write down what happens when we run this. t2 = TextMobject("Let's run them and write down\\\\" "each variable as we go...") t2.to_edge(LEFT) self.play(Write(t2), fbg.to_edge, RIGHT, {'buff': MED_SMALL_BUFF}) self.wait() t3 = TextMobject('Running foo() and bar() by hand').to_edge(UP) if high_quality: self.play(ReplacementTransform(VGroup(title, t2), t3)) else: self.play(FadeOut(title), FadeOut(t2), FadeIn(t3)) self.remove(title, t2) title = t3 self.wait() t4 = TextMobject('First, make a place\\\\to write each variable') t4.next_to(title, DOWN, buff=LARGE_BUFF).to_edge(LEFT) self.play(FadeIn(t4)) self.wait() foo_vars = VGroup(TexMobject('n:', '\\_')) foo_vars.next_to(foo_code, LEFT, buff=LARGE_BUFF * 2) bar_vars = VGroup( TexMobject('x:', '\\_'), TexMobject('y:', '\\_').shift(DOWN * .75), TexMobject('a:', '\\_').shift(DOWN * 1.5), TexMobject('b:', '\\_').shift(DOWN * 2.25), ) bar_vars.next_to(bar_code, LEFT, aligned_edge=TOP, buff=LARGE_BUFF * 2) self.play(Write(foo_vars)) self.play(Write(bar_vars)) self.wait() t5 = TextMobject("Start in foo()...") t5.move_to(t4, aligned_edge=LEFT) self.play(FadeOut(t4), FadeIn(t5)) self.wait() foo_code.move_hidden_highlight(2) self.play(FadeOut(t5), foo_code.highlight_lines, 2) self.wait() foo_n_q = TexMobject('?').move_to(foo_vars[0][1], aligned_edge=BOTTOM).shift(UP * 0.1) self.play(Write(foo_n_q)) foo_vars.add(foo_n_q) self.wait() xi = foo_code.pre_call(bar_code, (1, 3)) self.play(*foo_code.get_control_transfer_counterclockwise(xi), ) bar_code.post_control_transfer(xi, self) self.wait() bar_x = TexMobject('1').move_to(bar_vars[0][1], aligned_edge=BOTTOM).shift(UP * 0.1) bar_y = TexMobject('2').move_to(bar_vars[1][1], aligned_edge=BOTTOM).shift(UP * 0.1) a = Arrow(bar_code.get_code().get_lines(2), VGroup(bar_x, bar_y).get_right(), stroke_width=3) self.play(Write(bar_x), Write(bar_y), ShowCreationThenDestruction(a)) bar_vars_extras = VGroup() bar_vars_extras.add(bar_x, bar_y) self.play(bar_code.highlight_lines, 3) self.wait() bar_a = TexMobject('3').move_to(bar_vars[2][1], aligned_edge=BOTTOM).shift(UP * 0.1) a = Arrow(bar_code.get_code().get_lines(3), bar_a.get_right(), stroke_width=3) self.play(Write(bar_a), ShowCreationThenDestruction(a)) bar_vars_extras.add(bar_a) self.play(bar_code.highlight_lines, 4) self.wait() bar_b = TexMobject('6').move_to(bar_vars[3][1], aligned_edge=BOTTOM).shift(UP * 0.1) a = Arrow(bar_code.get_code().get_lines(4), bar_b.get_right(), stroke_width=3) self.play(Write(bar_b), ShowCreationThenDestruction(a)) bar_vars_extras.add(bar_b) self.play(bar_code.highlight_lines, 5) self.wait() xi = bar_code.pre_return(foo_code, 2) self.play(*bar_code.get_control_transfer_clockwise(xi), ) foo_code.post_control_transfer(xi, self) self.wait() foo_n = TexMobject('6').move_to(foo_vars[0][1], aligned_edge=BOTTOM).shift(UP * 0.1) a = Arrow(foo_code.get_code().get_lines(2), foo_n.get_right(), stroke_width=3) self.play( foo_code.highlight_lines, 3, ReplacementTransform(foo_n_q, foo_n), ShowCreationThenDestruction(a), ) foo_vars.add(foo_n) self.wait() # - Now give the variables "homes" in each function. Variables like # homes; they're warm and safe and sized just for them! print('Give variables homes') t1 = TextMobject('So how does the\\\\computer do this?').to_edge(LEFT) self.play(FadeIn(t1), foo_code.remove_highlight) self.wait(duration=2) t2 = TextMobject( 'Every variable is stored\\\\someplace in memory').to_edge(LEFT) new_title = TextMobject('Where are variables stored?').to_edge(UP) self.play( FadeOut(t1), FadeOut(title), FadeIn(new_title), FadeIn(t2), ) title = new_title self.wait() em = TextMobject('M') slot_height = em.get_height() + SMALL_BUFF * 2 slot_width = em.get_width() * 4 + SMALL_BUFF * 2 def build_var_home(name, value): var_name = TextMobject(name) slot_box = Rectangle(height=slot_height, width=slot_width, stroke_width=1) var_value = TextMobject(value) var_value.move_to(slot_box) var_name.next_to(slot_box, LEFT) stack_slot = VGroup(slot_box, var_name, var_value) return stack_slot foo_homes = build_var_home('n', '6') foo_homes.move_to(foo_vars, aligned_edge=LEFT) self.play(ReplacementTransform(foo_vars, foo_homes)) self.wait() bar_var_vals = [('x', '1'), ('y', '2'), ('a', '3'), ('b', '6')] bar_homes = VGroup() bar_homes_shift = 0 for n, v in bar_var_vals: bar_homes.add(build_var_home(n, v).shift(DOWN * bar_homes_shift)) bar_homes_shift += 0.75 bar_homes.move_to(bar_vars, aligned_edge=LEFT) t3 = TextMobject("Each variable lives in a\\\\``slot''").to_edge(LEFT) self.play( ReplacementTransform(bar_vars, bar_homes), # FadeOut(bar_vars), FadeOut(bar_vars_extras), FadeIn(bar_homes), ) self.wait() self.play( FadeOut(t2), FadeIn(t3), ) self.wait() # - Now arrange the homes into a "stack frame". print('Build stack frames') t4 = TextMobject( "All slots for a function\\\\are put together in a\\\\``frame''") t4.to_edge(LEFT) self.play(FadeOut(t3), FadeIn(t4)) frame_width = 2.8 foo_vars = [('n', '6')] foo_frame = StackFrame(foo_code, 'foo()', 3, foo_vars, width=frame_width) foo_frame.move_to(foo_homes, aligned_edge=LEFT).shift(LEFT * .5) self.play(ReplacementTransform(foo_homes, foo_frame)) self.wait() bar_frame = StackFrame(bar_code, 'bar(1,2)', 9, bar_var_vals, width=frame_width) bar_frame.move_to(bar_homes, aligned_edge=LEFT).shift(LEFT * .5) self.play(ReplacementTransform(bar_homes, bar_frame)) self.wait() t5 = TextMobject("\\textit{They're happy and warm\\\\together!}")\ .scale(0.75).next_to(t4, DOWN, buff=LARGE_BUFF) self.play(FadeIn(t5)) self.wait() # Cool, so where do frames live? print('Where do frames live') t6 = TextMobject('So where do frames live?').to_edge(LEFT) self.play( FadeOut(t5), FadeOut(t4), FadeIn(t6), ) self.wait() t7 = TextMobject("On the ``call stack''!").next_to(t6, DOWN, buff=LARGE_BUFF) self.play(FadeIn(t7)) self.wait() new_title = TextMobject('The Call Stack').to_edge(UP) t1 = TextMobject('Function calls push a new frame\\\\onto the stack')\ .to_edge(LEFT).shift(UP) t2 = TextMobject('Returning pops a frame off\\\\the stack').next_to( t1, DOWN, buff=LARGE_BUFF) 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) little_boxes.next_to(VGroup(foo_code, bar_code), LEFT, buff=LARGE_BUFF) self.play( ReplacementTransform(title, new_title), FadeOut(t6), FadeOut(t7), FadeOut(foo_frame), FadeOut(bar_frame), FadeIn(t1), LaggedStartMap(FadeInFrom, little_boxes, lambda m: (m, UP), lag_ratio=1.0, run_time=4.0), ) title = new_title self.wait() self.play( FadeIn(t2), LaggedStartMap(FadeOutAndShift, VGroup(*reversed(little_boxes)), lambda m: (m, UP), lag_ratio=1.0, run_time=4.0), ) self.wait(duration=1) t3 = TextMobject( "Let's run again and see\\\\the call stack in action...").to_edge( LEFT) t3.shift(UP) self.play(FadeOut(t1), FadeOut(t2), FadeIn(t3)) self.wait() print('Run with real call stack') # Let's also put main() into the picture. Start it off-frame upper right t4 = TextMobject("... and get main()\\\\into the picture.")\ .next_to(t3, DOWN, buff=LARGE_BUFF) main_code.next_to(title, DOWN, buff=MED_SMALL_BUFF).to_edge(RIGHT) main_code.shift(UP * 3 + RIGHT * 3) g = VGroup(main_code, foo_code, bar_code) self.play( g.arrange, DOWN, { 'aligned_edge': LEFT, 'buff': MED_SMALL_BUFF }, g.next_to, title, DOWN, {'buff': MED_SMALL_BUFF}, g.to_edge, RIGHT, FadeInFromDown(t4), ) self.wait(duration=2) t1 = TextMobject('Start in main()...') t1.next_to(title, DOWN, buff=LARGE_BUFF).to_edge(LEFT) frame_width = 3.0 args_ref = TextMobject('[ ]').scale(0.5) main_frame = StackFrame(main_code, 'main()', 3, [('args', args_ref)], width=frame_width) main_frame.next_to(g, LEFT, buff=LARGE_BUFF).to_edge(DOWN) main_code.move_hidden_highlight(3) self.play(FadeIn(t1), FadeInFromDown(main_frame), main_code.highlight_lines, 3, FadeOut(t3), FadeOut(t4)) self.wait() foo_frame = StackFrame(foo_code, 'foo()', 5, ['n'], width=frame_width) foo_frame.next_to(main_frame, UP, buff=SMALL_BUFF) b1 = BraceLabel(foo_frame, 'Calling foo()\\\\pushes a frame', brace_direction=LEFT, label_constructor=TextMobject) xi = main_code.pre_call(foo_code, 1) self.play( *main_code.get_control_transfer_counterclockwise(xi), FadeInFrom(foo_frame, UP), FadeInFrom(b1, UP), FadeOut(t1), ) foo_code.post_control_transfer(xi, self) self.wait(duration=2) self.play(foo_code.highlight_lines, 2, foo_frame.set_line, 6, FadeOut(b1)) self.wait() bar_frame = StackFrame(bar_code, 'bar(1, 2)', 10, ['x', 'y', 'a', 'b'], width=frame_width) bar_frame.next_to(foo_frame, UP, buff=SMALL_BUFF) b1 = BraceLabel(bar_frame, 'Calling bar()\\\\pushes a frame', brace_direction=LEFT, label_constructor=TextMobject) xi = foo_code.pre_call(bar_code, (1, 3)) self.play( *foo_code.get_control_transfer_counterclockwise(xi), FadeInFrom(bar_frame, UP), FadeInFrom(b1, UP), ) bar_code.post_control_transfer(xi, self) self.wait() self.play(bar_frame.update_slot, 'x', 1, bar_frame.update_slot, 'y', 2, FadeOut(b1)) self.play(bar_code.highlight_lines, 3, bar_frame.set_line, 11) self.wait() self.play(bar_frame.update_slot, 'a', 3) self.play(bar_code.highlight_lines, 4, bar_frame.set_line, 12) self.wait() self.play(bar_frame.update_slot, 'b', 6) self.play(bar_code.highlight_lines, 5, bar_frame.set_line, 13) b1 = BraceLabel(bar_frame, "Returning pops\\\\bar's frame", brace_direction=LEFT, label_constructor=TextMobject) self.play(FadeIn(b1)) self.wait(duration=2) xi = bar_code.pre_return(foo_code, 2) self.play( *bar_code.get_control_transfer_clockwise(xi), Uncreate(bar_frame), FadeOut(b1), ) foo_code.post_control_transfer(xi, self) self.wait() self.play(foo_code.highlight_lines, 3, foo_frame.set_line, 7, foo_frame.update_slot, 'n', 6) b1 = BraceLabel(foo_frame, "Returning pops\\\\foo's frame", brace_direction=LEFT, label_constructor=TextMobject) self.play(FadeIn(b1)) self.wait() xi = foo_code.pre_return(main_code, 3) self.play( *foo_code.get_control_transfer_clockwise(xi), Uncreate(foo_frame), FadeOut(b1), ) main_code.post_control_transfer(xi, self) self.wait() self.play(main_code.highlight_lines, 4, main_frame.set_line, 4) self.wait() t1 = TextMobject( 'And when main() returns\\\\the program ends').to_edge(LEFT) self.play(FadeIn(t1)) self.wait() off_screen_code = CodeBlock('Java', 'off_screen') off_screen_code.shift(UP * 8) # Offscreen up self.add(off_screen_code) xi = main_code.pre_return(off_screen_code, 1) self.play( *main_code.get_control_transfer_clockwise(xi), Uncreate(main_frame), FadeOut(t1), ) off_screen_code.post_control_transfer(xi, self) self.wait() t1 = TextMobject("Alright, let's do a more complicated one!") self.play( FadeInFromDown(t1), FadeOutAndShift(g, RIGHT), ) self.wait()