TDA 555型
DIT 440型
HT(高温)2019

函数编程导论
第2周的练习

第2周练习:递归和数据类型

以下是一些旨在帮助你练习写作和推理的练习关于递归定义和数据类型。

如果你没有时间做所有这些练习,不用担心。这些练习旨在提供足够的工作来保持经验丰富的学生很忙。如果你做了所有标有(*)的练习可能理解了本周的内容。

祝你好运!

1.(*)最大值函数

完成以下功能定义:

--maxi x y返回x和y的最大值

(我们称这个函数为maxi,因为有一个标准函数max做同样的事情。当然,您不应该使用它。)

首先为maxi写一个类型签名,左边写一个“等于undefined”。在编写定义之前,请考虑至少一个属性您将使用测试代码。

您需要考虑两种情况:它们是什么?写下左手边,并确保GHCi接受它们。

2.平方和。

定义一个函数,用于计算从1到n的数字的平方和。

--sumsq n返回1*1+2*2+…+n*n个

提示:使用递归从sumsq(n-1)计算sumsq n;不要使用一个公式(例如下面的公式)来计算这个值,而不需要递归。

有人说sumsq n等于n(n+1)(2n+1)/6。使用QuickCheck调查这是否成立。

3.(*)河内塔

河内塔是一个古老的谜,由一系列戒指组成不同尺寸,三根柱子安装在一个底座上。一开始戒指如图所示位于最左边的柱子上,目标是将它们全部移动到最右边的柱子,通过一次将一个环从一个柱子移动到另一个柱子。但是,任何时候都不能把大戒指放在小戒指的上面!

[河内塔:一个钉有8个环,两个空钉]

你能找到一种基于递归的解谜策略吗?那就是,如果你已经知道如何移动n个-从一根柱子到另一根柱子,你能找到移动的方法吗n个戒指?

如果你尝试一下你的策略,你会很快发现比如说,用五个圆环来解决这个难题需要很多步。你能定义Haskell函数

河内 n个
它计算将n个环从一个柱子移动到另一个使用你的策略?解决这个难题需要多少步十个戒指?

解决方案

传说原来的谜题有32个就在此时,一座修道院的布达迪斯僧侣正在解决这个问题。当拼图完成时,世界将终结。

难度更大(只有当你想要挑战时才这样做):现在假设我们在拼图中添加了第四个帖子。你能想出一个策略吗哪一个利用了第四个帖子?定义Haskell函数以计算你的新策略需要多少步才能解决这个难题。有多少步现在需要用十个环来解决这个难题吗?离结束还有多远如果僧侣们在他们的谜题中添加了第四个帖子,这将是世界的奇迹吗?

你能解释一下这种行为吗?提示:你的递归是什么类型的使用两种策略?

4.斐波那契数

这个斐波那契数由定义

F类0=1
F类1=1
F类n个+2=F类n个+1个+F类n个

因此,值的顺序从1、1、2、3、5、8……开始。。。

编写递归函数

--fib n计算第n个斐波那契数
基于上述数学定义。用它来计算第10、15、,第20、25和30斐波那契数。你注意到了什么?你能解释一下吗行为?

更困难:有一种更快的方法来计算斐波那契数。假设我们定义了一个函数fibAux,它满足

fibAux公司 (小谎 n个)(小谎(n个+1))== 小谎(n个+)

请注意,这是一个定义,它是一个属性!

从此属性你能看看我们会怎么做吗重新定义fib依据使用fibAux?(提示:尝试在属性中将n设置为0)。

有可能得到由此递归定义fibAux仅使用代数的属性:我们想要的两种情况是

fibAux公司 0  b = ...
fibAux公司   b | >0 = ...

看看你能不能用这个属性找出右手边是什么应该是。

使用QuickCheck测试fib的新定义(以fibAux表示)满足上述属性。

使用新版fib计算第10、15、20、25和30斐波那契数。你注意到了什么?

计算fibAux 4 1 1个手动操作。如果你以前编程过,然后观察i、a和b值连续变化的方式递归调用。它让你想起什么了吗?

5.因素。

质数第页只有两个因素,1和第页自身。一个复合数有两个以上的因子。定义函数

最小因子 n个

返回的最小因子n个大于一。例如,

最小因子 14 == 2
最小因子 15 == 

编程之前最小因子,至少编写两个QuickCheck它应该满足的属性。您需要整数的函数除法和余数:研究标准函数div公司国防部为此目的。

提示:如果你想练习递归,可以写最小因子通过使用辅助功能下一因子k n它返回n的最小因子大于k。您可以定义最小因子使用下一个因子、和下一个因子通过递归。

现在定义

num因子 n个

它计算范围1..n中n的因子数。

6.(*)定义类型

定义数据类型月份表示月份和函数

天月份 :: 月份 -> 整数 -> 整数
它计算一个月内的天数,同时给定年。(你可以忽略闰世纪之类的事情:只要假设第四年是闰年)。

定义数据类型日期,包含年、月和日,以及一个函数

有效日期 :: 日期 -> 布尔
那就回来了真的如果日期中的日期介于1和月份中的天数。

可选附加:还定义函数

明天 :: 日期 -> 日期
它计算给定日期的明天日期。

7.(*)复制

定义将给定单词复制n次的函数
复制 :: 整数 -> 字符串 -> 字符串
示例:
复制5“ha”“哈哈哈哈”
像往常一样,尽量缩小基本情况!

使用以下函数连接两个字符串:

(++):: 字符串 -> 字符串 -> 字符串
示例:
“flyg”++“计划”“飞行计划”

8.(*)乘法列表元素

定义函数

 :: 号码  =>[]-> 
将列表中的所有元素相乘。(想想:它应该是什么空列表的值为?)。例如
乘以[1,2,3,4,5]120

(这实际上是一个标准函数,称为产品).

9.避免重复

在许多情况下,列表不应包含重复元素。对于例如,一组卡片不应包含同一张卡片两次。定义一个功能

重复 :: 等式  =>[]-> 布尔

它返回真的如果其参数包含重复的元素。

重复[1,2,3,4,5]>False(错误)重复[1,2,3,2]真的

提示:标准函数元素,它测试元素是否出现在列表中,在这里很有用。

一种方法确保不包含重复项的列表以列出可能包含重复元素的列表,然后删除它们。定义函数

删除重复项 :: 等式  =>[]->[]
它返回一个列表,其中包含与其参数相同的元素,但没有重复项。使用以下属性进行测试:
prop_duplicates已删除 ::[整数]-> 布尔
prop_duplicates已删除 X轴 = (重复(删除重复项 X轴))

该物业是否保证删除重复项行为正确吗?如果没有,缺少什么?

(删除重复项实际上是一个标准函数,称为).