Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitedde1ea

Browse files
committed
Add start of functions chapter and basics of framework for functions in exercises
1 parent90dad4c commitedde1ea

File tree

6 files changed

+266
-19
lines changed

6 files changed

+266
-19
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# flake8: NOQA E501
2+
fromtypingimportList
3+
4+
frommain.exercisesimportgenerate_string
5+
frommain.textimportExerciseStep,MessageStep,Page,VerbatimStep
6+
frommain.utilsimportreturns_stdout
7+
8+
9+
classDefiningFunctions(Page):
10+
classdefine_greet(VerbatimStep):
11+
"""
12+
You've seen how to call functions such as `print()` and `len()`. Now you're going to learn how to write your own
13+
functions that you or other people can use. This is very important as programs get bigger and more complicated.
14+
15+
Here's a simple example:
16+
17+
__program_indented__
18+
19+
This defines a function called `greet` which accepts one parameter. Below the definition, we call the function twice.
20+
Run the code to see what happens.
21+
"""
22+
23+
defprogram(self):
24+
defgreet(name):
25+
print("Hello "+name+"!")
26+
27+
greet("Alice")
28+
greet("Bob")
29+
30+
classhow_are_you(VerbatimStep):
31+
"""
32+
A function definition is a compound statement. Like `if` and `for`, it has a header line followed by an indented body
33+
which can contain one or more statements.
34+
35+
Add another statement to the function so that it looks like this:
36+
37+
def greet(name):
38+
print("Hello " + name + "!")
39+
print("How are you?")
40+
41+
Then run the program again.
42+
"""
43+
44+
program_in_text=False
45+
46+
defprogram(self):
47+
defgreet(name):
48+
print("Hello "+name+"!")
49+
print("How are you?")
50+
51+
greet("Alice")
52+
greet("Bob")
53+
54+
classprint_twice_exercise(ExerciseStep):
55+
"""
56+
Note how the output of the program changed. `How are you?` is printed twice. You can think of the whole program as being
57+
equivalent to this:
58+
59+
name = "Alice"
60+
print("Hello " + name + "!")
61+
print("How are you?")
62+
63+
name = "Bob"
64+
print("Hello " + name + "!")
65+
print("How are you?")
66+
67+
This shows one of the most useful things about functions. They let you reuse the same code multiple times without
68+
having to repeat yourself. It's like writing a program within a program.
69+
70+
The header line of a function definition always has these parts:
71+
72+
1. The special keyword `def`, followed by a space.
73+
2. The name of the function. This is like a variable name - you can choose the name you want, but there are some constraints,
74+
e.g. it can't contain a space.
75+
3. A pair of parentheses `(` and `)`
76+
4. Zero or more parameter names between the parentheses, separated by commas if there's more than one. Here we have
77+
one parameter called `name`.
78+
5. A colon `:`
79+
80+
Here's an exercise: write a function called `print_twice` which accepts one argument `x` and prints that argument twice.
81+
82+
For example, `print_twice("Hello")` should output:
83+
84+
Hello
85+
Hello
86+
"""
87+
# TODO hints
88+
89+
function_name="print_twice"
90+
91+
@returns_stdout
92+
defsolution(self,x:str):
93+
print(x)
94+
print(x)
95+
96+
tests= {
97+
"Hello":"Hello\nHello\n",
98+
123:"123\n123\n",
99+
}
100+
101+
final_text="""
102+
Well done! You've reached the end for now. More coming soon!
103+
"""

‎backend/main/exercises.py‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,15 @@ def make_function(program, function_template):
5252
deffunc(**kwargs):
5353
exec(code,kwargs)
5454

55-
ifgetattr(function_template,"returns_stdout",False):
56-
func=returns_stdout(func)
57-
5855
returninitial_names,func
5956

6057

58+
defmatch_returns_stdout(func,solution):
59+
ifgetattr(solution,"returns_stdout",False):
60+
func=returns_stdout(func)
61+
returnfunc
62+
63+
6164
defcheck_exercise(func,solution,test,generate_inputs,functionise=False):
6265
test(solution)
6366
inputs= [generate_inputs()for_inrange(10)]
@@ -72,6 +75,8 @@ def check_exercise(func, solution, test, generate_inputs, functionise=False):
7275
exceptExerciseErrorase:
7376
returndict(message=str(e))
7477

78+
func=match_returns_stdout(func,solution)
79+
7580
try:
7681
expected_result=solution(**initial_names)
7782
exceptException:
@@ -84,6 +89,8 @@ def check_exercise(func, solution, test, generate_inputs, functionise=False):
8489
except:
8590
# Assume that the user can tell that the output is wrong
8691
returnFalse
92+
else:
93+
func=match_returns_stdout(func,solution)
8794

8895
try:
8996
test(func)
@@ -124,8 +131,7 @@ def check_result(func, inputs, expected_result):
124131

125132
ifresult!=expected_result:
126133
raiseExerciseError(f"""\
127-
Your code gives the right output for the example,
128-
but for these inputs:
134+
For these inputs:
129135
130136
{indented_inputs_string(inputs)}
131137

‎backend/main/test_transcript.json‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14933,5 +14933,118 @@
1493314933
]
1493414934
},
1493514935
"step":"upside_down_triangle_exercise"
14936+
},
14937+
{
14938+
"page":"Defining Functions",
14939+
"program": [
14940+
"def greet(name):",
14941+
" print(\"Hello\" + name +\"!\")",
14942+
"",
14943+
"greet(\"Alice\")",
14944+
"greet(\"Bob\")"
14945+
],
14946+
"response": {
14947+
"birdseye_url":null,
14948+
"message":"",
14949+
"passed":true,
14950+
"result": [
14951+
{
14952+
"color":"white",
14953+
"text":"Hello Alice!"
14954+
},
14955+
{
14956+
"color":"white",
14957+
"text":"\n"
14958+
},
14959+
{
14960+
"color":"white",
14961+
"text":"Hello Bob!"
14962+
},
14963+
{
14964+
"color":"white",
14965+
"text":"\n"
14966+
},
14967+
{
14968+
"color":"white",
14969+
"text":">>>"
14970+
}
14971+
]
14972+
},
14973+
"step":"define_greet"
14974+
},
14975+
{
14976+
"page":"Defining Functions",
14977+
"program": [
14978+
"def greet(name):",
14979+
" print(\"Hello\" + name +\"!\")",
14980+
" print(\"How are you?\")",
14981+
"",
14982+
"greet(\"Alice\")",
14983+
"greet(\"Bob\")"
14984+
],
14985+
"response": {
14986+
"birdseye_url":null,
14987+
"message":"",
14988+
"passed":true,
14989+
"result": [
14990+
{
14991+
"color":"white",
14992+
"text":"Hello Alice!"
14993+
},
14994+
{
14995+
"color":"white",
14996+
"text":"\n"
14997+
},
14998+
{
14999+
"color":"white",
15000+
"text":"How are you?"
15001+
},
15002+
{
15003+
"color":"white",
15004+
"text":"\n"
15005+
},
15006+
{
15007+
"color":"white",
15008+
"text":"Hello Bob!"
15009+
},
15010+
{
15011+
"color":"white",
15012+
"text":"\n"
15013+
},
15014+
{
15015+
"color":"white",
15016+
"text":"How are you?"
15017+
},
15018+
{
15019+
"color":"white",
15020+
"text":"\n"
15021+
},
15022+
{
15023+
"color":"white",
15024+
"text":">>>"
15025+
}
15026+
]
15027+
},
15028+
"step":"how_are_you"
15029+
},
15030+
{
15031+
"page":"Defining Functions",
15032+
"program": [
15033+
"def print_twice(x: str):",
15034+
" print(x)",
15035+
" print(x)"
15036+
],
15037+
"response": {
15038+
"birdseye_url":null,
15039+
"message":"",
15040+
"passed":true,
15041+
"result": [
15042+
{
15043+
"color":"white",
15044+
"text":">>>"
15045+
}
15046+
]
15047+
},
15048+
"step":"print_twice_exercise"
1493615049
}
1493715050
]

‎backend/main/tests.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def test_steps(self):
6666
self.assertEqual(
6767
response["passed"],
6868
notis_message,
69+
transcript[-1],
6970
)
7071

7172
self.assertEqual(

‎backend/main/text.py‎

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,20 @@
2525
frommain.utilsimportno_weird_whitespace,snake,unwrapped_markdown
2626

2727

28-
defclean_program(program,inputs=None):
28+
defclean_program(program,*,inputs=None,function_name=None):
2929
ifcallable(program):
3030
inputs=inputs_string(inputsor {})
3131
source=dedent(inspect.getsource(program))
32-
atok=ASTTokens(source,parse=True)
33-
func=atok.tree.body[0]
34-
lines=source.splitlines()[func.body[0].first_token.start[0]-1:]
35-
program=inputs+'\n'+dedent('\n'.join(lines))
32+
iffunction_name:
33+
assertsource.count("def solution(self, ")==1
34+
program=source.replace("def solution(self, ",f"def{function_name}(")
35+
# TODO strip annotations
36+
program=program.replace("@returns_stdout\n","")
37+
else:
38+
atok=ASTTokens(source,parse=True)
39+
func=atok.tree.body[0]
40+
lines=source.splitlines()[func.body[0].first_token.start[0]-1:]
41+
program=inputs+'\n'+dedent('\n'.join(lines))
3642
compile(program,"<program>","exec")# check validity
3743
no_weird_whitespace(program)
3844
returnprogram.strip()
@@ -52,7 +58,7 @@ def clean_step_class(cls, clean_inner=True):
5258
assertcls.tests
5359
# noinspection PyUnresolvedReferences
5460
inputs=list(cls.test_values())[0][0]
55-
program=clean_program(solution,inputs)
61+
program=clean_program(solution,inputs=inputs,function_name=cls.function_name)
5662
else:
5763
program=clean_program(program)
5864
assertprogram
@@ -261,18 +267,36 @@ def input_matches(self, pattern, remove_spaces=True):
261267

262268

263269
classExerciseStep(Step):
270+
function_name=None
264271

265272
defcheck(self):
266273
ifself.code_source=="shell":
267274
returnFalse
268275

269-
returncheck_exercise(
270-
self.input,
271-
self.solution,
272-
self.test_exercise,
273-
self.generate_inputs,
274-
functionise=True,
275-
)
276+
ifself.function_nameisNone:
277+
returncheck_exercise(
278+
self.input,
279+
self.solution,
280+
self.test_exercise,
281+
self.generate_inputs,
282+
functionise=True,
283+
)
284+
else:
285+
ifself.function_namenotinself.console.locals:
286+
returndict(message=f"You must define a function `{self.function_name}`")
287+
288+
func=self.console.locals[self.function_name]
289+
ifnotinspect.isfunction(func):
290+
returndict(message=f"`{self.function_name}` is not a function.")
291+
292+
# TODO check that function has correct signature
293+
294+
returncheck_exercise(
295+
func,
296+
self.solution,
297+
self.test_exercise,
298+
self.generate_inputs,
299+
)
276300

277301
@abstractmethod
278302
defsolution(self,*args,**kwargs):

‎backend/main/views.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def get_solution(self, page_index, step_index: int):
176176
page=pages[page_slugs_list[page_index]]
177177
step=getattr(page,page.step_names[step_index])
178178
ifissubclass(step,ExerciseStep):
179-
program=clean_program(step.solution)
179+
program=clean_program(step.solution,function_name=step.function_name)
180180
else:
181181
program=step.program
182182

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp