@@ -423,30 +423,12 @@ def check(self):
423423class introducing_len_and_range (VerbatimStep ):
424424"""
425425There you go. `words[4]` and beyond don't exist, so trying that will give you an error.
426-
427- By the way, you can get the number of elements in a list (commonly called the *length*) using `len(words)`.
428- That means that the last valid index of the list is `len(words) - 1`, so the last element is `words[len(words) - 1]`. Try these for yourself.
429-
430- So in general, the valid indices are:
431-
432- [0, 1, 2, ..., len(words) - 2, len(words) - 1]
433-
434- There's a handy built in function called `range` which gives you these values. Try it by running this code:
426+ That first program is a bit repetitive. Let's improve it with a list and a loop!
435427
436428__program_indented__
437429 """
438430
439- def program (self ):
440- for i in range (10 ):
441- print (i )
442-
443- class range_len (VerbatimStep ):
444- """
445- `range(n)` is similar to the list `[0, 1, 2, ..., n - 2, n - 1]`.
446- This gives us an alternative way to loop over a list:
447-
448- __program_indented__
449- """
431+ auto_translate_program = False
450432
451433predicted_output_choices = ["""\
452434 This
@@ -494,21 +476,272 @@ class range_len(VerbatimStep):
4944761
4954772
4964783
479+ """
480+ ]
481+
482+ def program (self ):
483+ words = ['This' ,'is' ,'a' ,'list' ]
484+ indices = [0 ,1 ,2 ,3 ]
485+
486+ for index in indices :
487+ print (index )
488+ print (words [index ])
489+
490+ class range_len (VerbatimStep ):
491+ """
492+ That's a bit better, but writing out `[0, 1, 2, ...]` isn't great, especially if it gets long.
493+ There's a handy function `range` to do that part for you. Replace `[0, 1, 2, 3]` with `range(4)`,
494+ i.e. `indices = range(4)`.
495+ """
496+
497+ program_in_text = False
498+
499+ auto_translate_program = False
500+
501+ def program (self ):
502+ words = ['This' ,'is' ,'a' ,'list' ]
503+ indices = range (4 )
504+
505+ for index in indices :
506+ print (index )
507+ print (words [index ])
508+
509+ class printing_the_range (VerbatimStep ):
510+ """
511+ As you can see, the result is the same. Try this:
512+
513+ __copyable__
514+ __program_indented__
515+ """
516+
517+ predicted_output_choices = ["""\
518+ 0
519+ 1
520+ 2
521+ 3
522+ """ ,"""\
523+ 1
524+ 2
525+ 3
526+ 4
527+ """ ,"""\
528+ [0]
529+ [1]
530+ [2]
531+ [3]
532+ """ ,"""\
533+ [1]
534+ [2]
535+ [3]
536+ [4]
537+ """ ,"""\
538+ This
539+ is
540+ a
541+ list
497542""" ,
498543 ]
499544
545+ def program (self ):
546+ indices = range (4 )
547+
548+ print (indices [0 ])
549+ print (indices [1 ])
550+ print (indices [2 ])
551+ print (indices [3 ])
552+
553+ class indices_out_of_bounds (VerbatimStep ):
554+ """
555+ Now try `__program__` in the shell.
556+ """
557+
558+ predicted_output_choices = ["0" ,"1" ,"2" ,"3" ,"4" ]
559+
560+ correct_output = "Error"
561+
562+ expected_code_source = "shell"
563+
564+ program = "indices[4]"
565+
566+ class range_almost_the_same_as_list (VerbatimStep ):
567+ """
568+ `range(4)` is the same thing as `[0, 1, 2, 3]` ... almost. Try `__program__` in the shell.
569+ """
570+
571+ expected_code_source = "shell"
572+
573+ program = "range(4)"
574+
575+ class range_versus_list (VerbatimStep ):
576+ """
577+ That's probably a bit surprising. If you're curious, the `0` represents the start of the range.
578+ `0` is the default start, so `range(4)` is equal to `range(0, 4)`.
579+ `4` is the end of the range, but the end is always excluded, so the last value is `3`.
580+ If you're confused now, don't worry about it.
581+
582+ There's a good reason for why `range(4)` is not actually a list - it makes programs faster and more efficient.
583+ It's not worth explaining that more right now.
584+
585+ But you can easily convert it to a list: try `__program__` in the shell.
586+ """
587+ predicted_output_choices = [
588+ "range(4)" ,
589+ "range(0, 4)" ,
590+ "list(range(4))" ,
591+ "list(range(0, 4))" ,
592+ "range(0, 1, 2, 3)" ,
593+ "(0, 1, 2, 3)" ,
594+ "[0, 1, 2, 3]"
595+ ]
596+
597+ expected_code_source = "shell"
598+
599+ program = "list(range(4))"
600+
601+ class using_len_first_time (VerbatimStep ):
602+ """
603+ That's just a demonstration to let you see a range in a more familiar form.
604+ You should almost never actually do that.
605+
606+ If you're feeling overwhelmed, don't worry! All you need to know is that `range(n)`
607+ is very similar to the list:
608+
609+ [0, 1, 2, ..., n - 2, n - 1]
610+
611+ By the way, you can get the number of elements in a list (commonly called the *length*) using the `len` function.
612+ Try it by running this code:
613+
614+ __copyable__
615+ __program_indented__
616+ """
617+
500618auto_translate_program = False
501619
620+ predicted_output_choices = ["0" ,"1" ,"2" ,"3" ,"4" ,"5" ]
621+
502622def program (self ):
503623words = ['This' ,'is' ,'a' ,'list' ]
624+ print (len (words ))
625+
626+ class print_last_element (ExerciseStep ):
627+ """
628+ Exercise: for any non-empty list `words`, print the last element. For example, if
629+
630+ __no_auto_translate__
631+ words = ['This', 'is', 'a', 'list']
632+
633+ your program should print `list`.
634+ """
635+
636+ hints = """
637+ To access the last element of the list, you'll need the index of the last position.
638+ If the list has 2 elements, the first element is at index 0, so the last element is at index 1.
639+ Likewise, if the list had 3 elements, the last element would be at index 2.
640+ Do you see a pattern between those numbers? How can you express it?
641+ Can you come up with a general solution that works for any length?
642+ """
643+
644+ def solution (self ,words :List [str ]):
645+ print (words [len (words )- 1 ])
646+
647+ tests = [
648+ (["Python" ],"Python" ),
649+ (['Hello' ,'world' ],"world" ),
650+ (['futurecoder' ,'is' ,'cool!' ],"cool!" ),
651+ (['This' ,'is' ,'a' ,'list' ],"list" )
652+ ]
653+
654+ class print_indices_and_words (ExerciseStep ):
655+ """
656+ So in general, the valid indices are:
657+
658+ [0, 1, 2, ..., len(words) - 2, len(words) - 1]
659+
660+ Now we can fix the program from earlier to work with any list. Fill in the `...`:
504661
662+ __copyable__
663+ __no_auto_translate__
664+ words = ['This', 'is', 'a', 'list']
665+
666+ for index in ...:
667+ print(index)
668+ print(words[index])
669+
670+ For the given example value of `words` it should print:
671+
672+ 0
673+ This
674+ 1
675+ is
676+ 2
677+ a
678+ 3
679+ list
680+ """
681+
682+ hints = """
683+ Remember that earlier we used `range(4)`.
684+ This time, it should work for any list. What if the list has 5 elements, or 10?
685+ Combine the two functions you learned!
686+ """
687+
688+ def solution (self ,words :List [str ]):
505689for index in range (len (words )):
506690print (index )
507691print (words [index ])
508692
693+ tests = [
694+ (['Python' ],"""\
695+ 0
696+ Python
697+ """ ),
698+ (['Hello' ,'world' ],"""\
699+ 0
700+ Hello
701+ 1
702+ world
703+ """ ),
704+ (['futurecoder' ,'is' ,'cool!' ],"""\
705+ 0
706+ futurecoder
707+ 1
708+ is
709+ 2
710+ cool!
711+ """ ),
712+ (['This' ,'is' ,'a' ,'list' ],"""\
713+ 0
714+ This
715+ 1
716+ is
717+ 2
718+ a
719+ 3
720+ list
721+ """ ),
722+ ]
723+
724+ final_text = """
725+ If you're still not quite comfortable with `range` and/or `len`, practice and experiment with it for a bit.
726+ Here are some simple exercises you can try on your own if you want.
727+
728+ - Print the numbers from `1` to `100` inclusive.
729+ - Print your name 100 times.
730+ - Print each word in a list `words` except for the last one.
731+ - Print each word in `words` in reverse order, i.e. print the last word, then the second last word, etc.
732+ - Revisit the bonus problem at the end of the [Introducing Lists page](#IntroducingLists),
733+ whether or not you completed it. It's now much easier with `range` and `len`!
734+
735+ When you're ready, continue to the next page for something a bit more challenging.
736+ """
737+
738+
739+ class GettingElementsAtPositionExercises (Page ):
740+ title = "Exercises with `range()` and `len()`"
741+
509742class index_exercise (ExerciseStep ):
510743"""
511- Let's get some exercise! Given a list `things` and a value `to_find`,
744+ Given a list `things` and a value `to_find`,
512745print the first index of `to_find` in the list, i.e. the lowest number `i` such that
513746`things[i]` is `to_find`. For example, for
514747