编程起步:(二)告诉机器一个函数
可以教初中生编程,但不能是那种Hello World
编程;编程的概念也可以被初中生理清,但不能一股脑地把莫名其妙的概念塞到他们的脑海。
函数这个概念就是一例。如果你对上篇编程起点:(一)告诉机器一个功能有印象,那么应该还记得函数从“功能”(function)派生而来,也学习了单参数输入的函数与调用,函数、参数的命名与赋值,还有控制台的使用方法。
如果对这些印象有些模糊,请再回去读一遍。如果记得,请活动一下脑子:
- 打开控制台
- 输入任意一个函数
- 用任意输入调用这个函数
- 每次调用都要重新输入这个函数,用什么方法可以缓解这个问题?
如果有任何一个有点模糊,请阅读编程起点:(一)告诉机器一个功能
本篇结构与练习方式
本篇主要通过一系列练习,打磨函数中输入与输出的概念。这是学习中痛苦而必要的一环。希望你能在解题中收获一点正反馈。
接下来每一节都会遇到新的一种函数,文中会给出一个例子,请你都想一想如何调用,写出具体调用的例子,然后逐步求值,最后用控制台验证。然后才看答案。就像之前讲x => x * x
一样。
例题后面会附上几个练习,请你练习这个 “调用-逐步求值-验证” 过程。你可以多写几个类似的函数,重复这一过程。
练习是很私人的。为了达到最佳效果,最好自己尽力做了练习,再去寻求帮助或解答。
对不同程度的练习,“尽力”的标准不一样。有例题参考的条件下,本篇每道题都不建议花超过五分钟。如果超过了,可以考虑思考的方向对不对。
多个输入
之前的函数,输入数量没有大于一个的。
例题:“调用-逐步求值-验证” 函数(x, y) => x - y
。
如果想自行做出例题,请及时停止下拉或挡住下方,后续例题也是一样。
先观察函数本身,对比已经熟悉的x => x * x
:
- 同样是用箭头
=>
分隔输入输出; - 输入有
x
、y
两个,为避免歧义用英文括号包起来,同时用英文逗号,
分隔; - 输出是
x - y
。
再研究调用,与(x => x * x)(2)
的主要区别还是在输入上,((x, y) => x - y)(2, 3)
。
接着是逐步求值
((x, y) => x - y)(2, 3)
(x=2, y=3) => x - y
2 - 3
-1
跟之前一样,也是通过替换输出中的输入求值。
最后通过控制台求值,与上述结果一致
为文章简洁,后续例题仅列出逐步求值过程。
练习:“调用-逐步求值-验证” 如下函数
(y, x) => x - y
(x, y, z) => x / y + y / z - z / x
输出为函数
之前的函数,输出都是数值
例题:“调用-逐步求值-验证” 函数x => (y => x - y)
。
如果想自行做出例题,请及时停止下拉或挡住下方,后续例题也是一样。
一些初学者一看到两个箭头就慌了,但你只要抓住=>
分隔输入输出的定义就行。也就是说,这个函数的输入是x
,输出是(y => x - y)
。尝试用x
调用一次:
(x => (y => x - y))(2)
(x=2) => (y => x - y)
y => 2 - y
输出是个函数,而且如果你在控制台做了这个练习,会得到略微不一样的结果。请以上述逐步求值结果为准。
练习: 逐步求出(x => (y => x - y))(2)(3)
的值,并验证结果。
用什么方法可以缓解看起来复杂的问题?一种方法就是命名,给输出的函数命名。比如
let f = y => x - y
,你可以用这样的思路,再做一次上述练习。
练习:“调用-逐步求值-验证” 如下函数
a => b => a + b
(x, y) => z => x * z - y
输入为函数
之前的函数,输入都是数值
例题:“调用-逐步求值-验证” 函数let call = (func, x) => func(x)
,其中func
代表函数。
如果想自行做出例题,请及时停止下拉或挡住下方,后续例题也是一样。
call(x => x * x, 2)
(x => x * x)(2)
(x=2) => x * x
2 * 2
4
思考题:
call
对应之前讲的哪个概念?call((x, y) => x + y, (4, 5))
符合语法么?如果符合,请逐步求值并验证;如果不符合,要怎样修改call
或调用方式?
练习:“逐步求值-验证” 如下调用:
call(y => x => x + y, 1)
call(x => x * x, call(x => x + 1, 2))
你可以考虑休息一下,后面会更难一些。
输入与输出都是函数
例题:“调用-逐步求值-验证” 函数let compose = (outer, inner) => x => outer(inner(x))
。
如果喜欢中文:
let 组合函数 = (外函数, 内函数) => x => 外函数(内函数(x))
如果想自行做出例题,请及时停止下拉或挡住下方。
compose(x => x * x, x => x - 1)(2)
(x => x * x) ((x => x - 1)(2))
(x => x * x) ((x=2) => x - 1)
(x => x * x) ((x=2) => x - 1)
(x => x * x) (2 - 1)
(x => x * x) (1)
(x=1) => x * x
1 * 1
1
思考题:
- 如何消去
compose
并简化compose(x => x * x, y => y - 1)
练习:“调用-逐步求值-验证” 如下函数
compose(y => y * y, z => z - 1)(3)
compose(x => x * x, x => x * x)(2)
小结
本文通过一些练习,配上模板化的解法,逐步打磨对函数的理解,尤其是函数作为输入或输出。可以通过目录回顾:
至此,你应当掌握了学习函数的基本概念。
思考题:
- 你在这两篇文章里见过输入数量为零的函数吗?你觉得还有别的输入或输出类型的函数么?
- 你更喜欢把这两节讨论的概念叫做“函数”,还是“功能”,为什么?
- 如果一个人说了一句“玫瑰不叫玫瑰,依然芳香”,然后说叫“函数”还是“功能”都不重要,你认同他的观点么?
下一节是《编程起步:(三) 功能还是函数,这是一个问题》 ,它的末尾附有本节部分思考题答案。