|
1 | 1 | # flake8: NOQA E501 |
2 | 2 | importast |
| 3 | +fromrandomimportchoice,randint |
3 | 4 | fromstringimportascii_uppercase |
4 | 5 | fromtypingimportList |
5 | | -fromrandomimportchoice,randint |
6 | 6 |
|
7 | 7 | frommain.textimportExerciseStep,Page,MessageStep,Disallowed,VerbatimStep |
8 | 8 |
|
9 | 9 |
|
10 | 10 | defgenerate_board(board_type): |
11 | 11 | winning=choice([True,False]) |
12 | | -size=randint(3,10) |
| 12 | +size=randint(3,9) |
13 | 13 | char1=choice(ascii_uppercase) |
14 | 14 | char2=choice(ascii_uppercase) |
15 | 15 | chars= [char1,char2,' '] |
@@ -809,7 +809,7 @@ def format_board(board): |
809 | 809 | joined_rows.append("".join(row)) |
810 | 810 | return "\\n".join(joined_rows) |
811 | 811 |
|
812 | | -If you'd like, you can just continue to the next page now. Or you can do a bonus challenge! |
| 812 | +If you'd like, you can just continue to the[next page](/course/?page=Types) now. Or you can do a bonus challenge! |
813 | 813 |
|
814 | 814 | Write an improved version of `format_board` that displays row and column separators. For example, if |
815 | 815 |
|
@@ -863,8 +863,6 @@ def format_board(board): |
863 | 863 |
|
864 | 864 | parsons_solution=True |
865 | 865 |
|
866 | | -# TODO link to next page |
867 | | - |
868 | 866 | defsolution(self): |
869 | 867 | defformat_board(board:List[List[str]]): |
870 | 868 | joined_rows= [] |
@@ -908,3 +906,300 @@ def generate_inputs(cls): |
908 | 906 | Next you will learn more about types in Python and how to convert them, and how to get input from the players. |
909 | 907 | You are already about halfway done with the project. Keep going! |
910 | 908 | """ |
| 909 | + |
| 910 | + |
| 911 | +classTypes(Page): |
| 912 | +classfive_different_types(VerbatimStep): |
| 913 | +""" |
| 914 | +So far we've seen various kinds of data: strings, lists, numbers and booleans. |
| 915 | +These are called *types*. Every value has a type which affects how it behaves |
| 916 | +and can be revealed with the `type` function: |
| 917 | +
|
| 918 | + __copyable__ |
| 919 | + __program_indented__ |
| 920 | + """ |
| 921 | + |
| 922 | +defprogram(self): |
| 923 | +print(type('Hello World')) |
| 924 | +print(type(23)) |
| 925 | +print(type(True)) |
| 926 | +print(type([1,2,3])) |
| 927 | +print(type(4.56)) |
| 928 | + |
| 929 | +classcheck_type_manually(VerbatimStep): |
| 930 | +""" |
| 931 | +Python reports first that `type('Hello World')` is `<class 'str'>`. Don't worry about `class` for now. |
| 932 | +`str` is short for *string*. |
| 933 | +
|
| 934 | +Then `True` is a `bool` (short for *boolean*) and `[1, 2, 3]` has type `list`. |
| 935 | +
|
| 936 | +Note that there are two types for numbers: |
| 937 | +
|
| 938 | +- `int`, short for 'integer', is for whole numbers, meaning no fractions or decimal points. |
| 939 | +- `float`, short for 'floating point number', is for numbers with a decimal point and maybe a fractional part |
| 940 | +
|
| 941 | +In most cases you don't have to worry about the different types of number, as you can mix the two when doing maths. |
| 942 | +
|
| 943 | +Types are values which can be used in various ways, just like other values. |
| 944 | +For example, try this in the shell: |
| 945 | +
|
| 946 | +__program_indented__ |
| 947 | + """ |
| 948 | + |
| 949 | +expected_code_source='shell' |
| 950 | + |
| 951 | +program='type(3) == int' |
| 952 | + |
| 953 | +classdifferent_types_look_same(VerbatimStep): |
| 954 | +""" |
| 955 | +Values with different types are usually quite different from each other, but they can look the same when printed, |
| 956 | +which can be confusing. Try this: |
| 957 | +
|
| 958 | + __copyable__ |
| 959 | + __program_indented__ |
| 960 | +
|
| 961 | +(You can use `print(repr(123))` and `print(repr('123'))` to tell the difference. What's `repr`? Google it!) |
| 962 | + """ |
| 963 | + |
| 964 | +defprogram(self): |
| 965 | +print('123') |
| 966 | +print(123) |
| 967 | +print(123=='123') |
| 968 | + |
| 969 | +classplus_has_two_meanings(VerbatimStep): |
| 970 | +""" |
| 971 | +Different types have different methods and support different operators. |
| 972 | +The same method or operator can also mean different things. |
| 973 | +For example, see how `+` has different meanings for `str` and `int`: |
| 974 | +
|
| 975 | + __copyable__ |
| 976 | + __program_indented__ |
| 977 | + """ |
| 978 | + |
| 979 | +predicted_output_choices= [ |
| 980 | +"579\n579", |
| 981 | +"579\n'579'", |
| 982 | +"123456\n123456", |
| 983 | +"123456\n'123456'", |
| 984 | +"579\n123456", |
| 985 | +"579\n'123456'", |
| 986 | + ] |
| 987 | + |
| 988 | +defprogram(self): |
| 989 | +print(123+456) |
| 990 | +print('123'+'456') |
| 991 | + |
| 992 | +classless_than_has_two_meanings(VerbatimStep): |
| 993 | +""" |
| 994 | +For two integers `+` acts as addition, whereas for two strings it acts as string concatenation. |
| 995 | +Python automatically figures out the meaning of `+` from the types of the inputs. |
| 996 | +Similarly `<` acts differently on two strings and two integers: |
| 997 | +
|
| 998 | + __copyable__ |
| 999 | + __program_indented__ |
| 1000 | + """ |
| 1001 | + |
| 1002 | +predicted_output_choices= [ |
| 1003 | +"True\nTrue", |
| 1004 | +"True\nFalse", |
| 1005 | +"False\nTrue", |
| 1006 | +"False\nFalse", |
| 1007 | + ] |
| 1008 | + |
| 1009 | +defprogram(self): |
| 1010 | +print(13<120) |
| 1011 | +print('13'<'120') |
| 1012 | + |
| 1013 | +classless_than_sorting_strings(VerbatimStep): |
| 1014 | +""" |
| 1015 | +So `<` acts as the usual 'less than' between two integers, because `13` is less than `120`, |
| 1016 | +but it acts as the dictionary ordering between two strings: `13` is 'alphabetically' after `120` |
| 1017 | +because `3` comes after `2`. |
| 1018 | +
|
| 1019 | +See what difference this makes when sorting a list: |
| 1020 | +
|
| 1021 | + __copyable__ |
| 1022 | + __program_indented__ |
| 1023 | + """ |
| 1024 | + |
| 1025 | +predicted_output_choices= [ |
| 1026 | +"[0, 13, 120]\n['0', '120', '13']", |
| 1027 | +"[0, 13, 120]\n['13', '120', '0']", |
| 1028 | +"[0, 13, 120]\n['120', '13', '0']", |
| 1029 | +"[120, 13, 0]\n['0', '120', '13']", |
| 1030 | +"[120, 13, 0]\n['13', '120', '0']", |
| 1031 | +"[120, 13, 0]\n['120', '13', '0']", |
| 1032 | + ] |
| 1033 | + |
| 1034 | +defprogram(self): |
| 1035 | +print(sorted([120,13,0])) |
| 1036 | +print(sorted(['120','13','0'])) |
| 1037 | + |
| 1038 | +classcommon_type_errors(VerbatimStep): |
| 1039 | +""" |
| 1040 | +What happens if you use an operator between a `str` and an `int`? Try in the shell: |
| 1041 | +
|
| 1042 | +__program_indented__ |
| 1043 | + """ |
| 1044 | + |
| 1045 | +correct_output="Error" |
| 1046 | + |
| 1047 | +predicted_output_choices= ["46","'46'","1234","'1234'"] |
| 1048 | + |
| 1049 | +expected_code_source="shell" |
| 1050 | + |
| 1051 | +program="12 + '34'" |
| 1052 | + |
| 1053 | +defcheck(self): |
| 1054 | +return"TypeError: unsupported operand type(s) for +: 'int' and 'str'"inself.result |
| 1055 | + |
| 1056 | +classfixing_type_errors_with_conversion(ExerciseStep): |
| 1057 | +""" |
| 1058 | +Using a string instead of an integer in `range` like `range('5')`, |
| 1059 | +or in list subscripting like `list['3']` will also lead to an error. |
| 1060 | +
|
| 1061 | +Most of these problems can be solved by converting the string to an integer by using `int` as a function: |
| 1062 | +`int('5')` will return the integer `5`. |
| 1063 | +Similarly an integer can be converted to a string by using `str` as a function: |
| 1064 | +`str(5)` will return the string `'5'`. |
| 1065 | +
|
| 1066 | +Using this new knowledge, fix this broken program: |
| 1067 | +
|
| 1068 | + __copyable__ |
| 1069 | + number = '3' |
| 1070 | + for i in range(number): |
| 1071 | + print('Starting... ' + i + 1) |
| 1072 | + print('Go!') |
| 1073 | +
|
| 1074 | +The correct program should print: |
| 1075 | +
|
| 1076 | + Starting... 1 |
| 1077 | + Starting... 2 |
| 1078 | + Starting... 3 |
| 1079 | + Go! |
| 1080 | +
|
| 1081 | +Your solution should work for any value of the variable `number`. |
| 1082 | +
|
| 1083 | + """ |
| 1084 | + |
| 1085 | +hints=""" |
| 1086 | +At what points is this code broken? |
| 1087 | +There are values that need to be converted to a different type. |
| 1088 | +Specifically there's a `str` that needs to be converted to an `int`. |
| 1089 | +And an `int` that needs to be converted to a `str`. |
| 1090 | + """ |
| 1091 | + |
| 1092 | +tests= [ |
| 1093 | + ('1',"""\ |
| 1094 | +Starting... 1 |
| 1095 | +Go! |
| 1096 | + """), |
| 1097 | + ('2',"""\ |
| 1098 | +Starting... 1 |
| 1099 | +Starting... 2 |
| 1100 | +Go! |
| 1101 | + """), |
| 1102 | + ('3',"""\ |
| 1103 | +Starting... 1 |
| 1104 | +Starting... 2 |
| 1105 | +Starting... 3 |
| 1106 | +Go! |
| 1107 | + """), |
| 1108 | + ] |
| 1109 | + |
| 1110 | +disallowed=Disallowed(ast.JoinedStr,label="f-strings") |
| 1111 | + |
| 1112 | +defsolution(self,number:str): |
| 1113 | +foriinrange(int(number)): |
| 1114 | +print('Starting... '+str(i+1)) |
| 1115 | +print('Go!') |
| 1116 | + |
| 1117 | +@classmethod |
| 1118 | +defgenerate_inputs(cls): |
| 1119 | +return { |
| 1120 | +"number":str(randint(1,10)) |
| 1121 | + } |
| 1122 | + |
| 1123 | +classformat_board_with_numbers(ExerciseStep): |
| 1124 | +""" |
| 1125 | +Write an improved version of `format_board` that has row and column numbers like this: |
| 1126 | +
|
| 1127 | + 123 |
| 1128 | + 1XOX |
| 1129 | + 2 OO |
| 1130 | + 3 X |
| 1131 | +
|
| 1132 | +It should work for boards of any size. We provide a test case: |
| 1133 | +
|
| 1134 | + __copyable__ |
| 1135 | + def format_board(board): |
| 1136 | + ... |
| 1137 | +
|
| 1138 | + assert_equal( |
| 1139 | + format_board([ |
| 1140 | + ['X', 'O', 'X'], |
| 1141 | + ['O', ' ', ' '], |
| 1142 | + [' ', 'X', 'O'] |
| 1143 | + ]), |
| 1144 | + ' 123\\n1XOX\\n2O\\n3 XO' |
| 1145 | + ) |
| 1146 | +
|
| 1147 | + """ |
| 1148 | + |
| 1149 | +hints=""" |
| 1150 | +You can start by using the ideas from your previous solution to `format_board`. Using `join` is highly recommended! |
| 1151 | +The first line has to be treated separately from the rest. |
| 1152 | +Remember that `range` yields numbers in the way: 0, 1, 2, ... |
| 1153 | +We want numbers on the first line like this: 1, 2, 3... |
| 1154 | +Each number has to be converted to a string before being added to the first row! |
| 1155 | +For the rows of the board itself, do something similar. |
| 1156 | +Start with a list consisting only of the first line that you built above. |
| 1157 | +Add each row's string to the list, then join the list with a newline character. |
| 1158 | + """ |
| 1159 | + |
| 1160 | +parsons_solution=True |
| 1161 | + |
| 1162 | +defsolution(self): |
| 1163 | +defformat_board(board:List[List[str]]): |
| 1164 | +first_row=' ' |
| 1165 | +foriinrange(len(board)): |
| 1166 | +first_row+=str(i+1) |
| 1167 | +joined_rows= [first_row] |
| 1168 | +foriinrange(len(board)): |
| 1169 | +joined_row=str(i+1)+''.join(board[i]) |
| 1170 | +joined_rows.append(joined_row) |
| 1171 | +return"\n".join(joined_rows) |
| 1172 | + |
| 1173 | +returnformat_board |
| 1174 | + |
| 1175 | +@classmethod |
| 1176 | +defgenerate_inputs(cls): |
| 1177 | +return { |
| 1178 | +"board":generate_board('row') |
| 1179 | + } |
| 1180 | + |
| 1181 | +tests= [ |
| 1182 | + ([[" "," "," "], |
| 1183 | + ["X","X","O"], |
| 1184 | + ["O","O","X"]]," 123\n1\n2XXO\n3OOX"), |
| 1185 | + ([["X","X","X","X"], |
| 1186 | + ["O","O","X"," "], |
| 1187 | + [" ","X","O","X"], |
| 1188 | + [" ","O"," ","X"]]," 1234\n1XXXX\n2OOX\n3 XOX\n4 O X"), |
| 1189 | + ([["X","O"," ","X","X"], |
| 1190 | + ["X","O"," ","X","X"], |
| 1191 | + [" ","O","X","X"," "], |
| 1192 | + ["X","X","X","X"," "], |
| 1193 | + ["X","O","O","X","O"]], |
| 1194 | +" 12345\n1XO XX\n2XO XX\n3 OXX\n4XXXX\n5XOOXO"), |
| 1195 | + ] |
| 1196 | + |
| 1197 | +final_text=""" |
| 1198 | +Excellent! |
| 1199 | +
|
| 1200 | +By the way, when you need to concatenate strings and numbers, remember that you can also |
| 1201 | +use f-strings. They often look nicer. |
| 1202 | +
|
| 1203 | +You've learned about types in Python and how to avoid common errors by converting types. |
| 1204 | +Keep going with the rest of the project! |
| 1205 | + """ |