我正在尝试在Haskell中编写一个可以确定某人体重指数的简单程序。
这是我写的:
type Height = Float
type Weight = Float
type PeopleStats = [(String, Height, Weight)]
和...
bmi :: Height -> Weight -> Float
bmi heightCm weightKg = weightKg/(heightCm)^2
healthy :: Height -> Weight -> Bool
healthy heightCm weightKg | 25 > index && 18 < index = True
| otherwise = False
where index = bmi heightCm weightKg
到目前为止,“健康”功能可以计算某人的BMI,而“健康人”功能会返回一个布尔语句,用于确定该人的BMI是否在健康人认为正常的范围内。
我想写一个名为“healthyPeople”的函数。
healthyPeople :: PeopleStats -> [String]
此功能需要获取PeopleStats列表,并返回“健康”功能中被认为“健康”的人的名字(字符串)列表。
例如:
如果我输入[("Lee", 65, 185), ("Wang", 170, 100), ("Tsu", 160, 120)]
,我将获得BMI从“健康”中的布尔函数返回true的人的名单。
请帮助!!!!
答案 0 :(得分:5)
首先,我认为您可能打算将bmi
定义为:
bmi :: Height -> Weight -> Float
bmi heightCm weightKg = weightKg/(heightCm/100)^2
因为公式使用以米为单位的高度。
现在,这是使用辅助函数一步一步的方法。我定义了一个类型:
type PersonStats = (String, Height, Weight)
以及该类型的一些函数:
healthyPerson :: PersonStats -> Bool
healthyPerson (name, h, w) = healthy h w
getName :: PersonStats -> String
getName (name, h, w) = name
有了这些,最终的功能就变得微不足道了:
healthyPeople :: PeopleStats -> [String]
healthyPeople people = map getName $ filter healthyPerson people
或以无点符号表示:
healthyPeople :: PeopleStats -> [String]
healthyPeople = map getName . filter healthyPerson
首先从列表中筛选出健康的人,然后将统计信息列表映射到名称列表中 如果你使用lambdas,你可以在没有帮助者的情况下一次性表达整个函数。
答案 1 :(得分:2)
有一个名为filter
的标准Haskell函数可以完全(几乎)完成您想要的操作。它具有类型(a -> Bool) -> [a] -> [a]
,即它接受谓词和列表并返回满足谓词的成员。
您无法直接将其应用于PeopleStats
,因为类型不匹配,但编写连接两者的函数并不难:
healthyPerson :: (String, Height, Weight) -> Bool
healthyPerson (_, h, w) = healthy h w
healthyPeople :: [(String, Height, Weight)] -> [String]
healthyPeople people = map name $ filter healthyPerson people
where name (s, _, _) = s
这就是你想要的。
答案 2 :(得分:0)
首先请注意,您对BMI的定义不正确 - 您需要将厘米转换为米:
bmi heightCm weightKg = weightKg/(heightCm/100)^2
修好后,我想出了以下内容:
healthyPeople :: PeopleStats -> [String]
healthyPeople [] = []
healthyPeople ((name, height, weight):xs) | healthy height weight = name : healthyPeople xs
| otherwise = healthyPeople xs
这是相当直接的。它使用list-based recursion来递归列表的所有元素,它使用类似于在健康函数中使用它们的方式来根据列表头部的人是否健康来切换行为。如果它们是健康的,那么它们的名称是concatted with处理列表其余部分的结果。
下次,您应该尝试自己解决问题,然后寻求帮助(并显示您尝试过的内容)。你会学到更多!
答案 3 :(得分:0)
让我们考虑一下你想做什么。您有一个列表,并且您希望(a)仅从列表中选择某些项目,以及(b)对列表中的每个元素执行某些操作。这是Haskell,让我们用类型来表达。你需要的第一件事 - 好吧,它必须采取一个列表[a]
,并检查每个元素是否良好。怎么检查?好吧,它应该是一个函数a -> Bool
。它应该给我们一个较小的清单。换句话说,像[a] -> (a -> Bool) -> [a]
之类的东西。然后我们想拿出我们的列表并对每个元素做一些事情。换句话说,我们需要一个列表[a]
和一个函数a -> b
。因此,我们需要[a] -> (a -> b) -> [b]
类型的东西。既然我们有类型,我们就是黄金:我们可以使用Hoogle来搜索它们。我高度,强烈推荐定期使用Hoogle;它是一个Haskell搜索引擎,它搜索类型 - 唯一令人敬畏的部分和函数/数据类型/类型类/模块/包名称。事实证明,first function是查询的第二个结果:filter :: (a -> Bool) -> [a] -> [a]
。这将获取一个函数和一个列表,并仅返回函数为true的列表元素。 second function是第一个结果map :: (a -> b) -> [a] -> [b]
,它在给定列表的每个元素上调用给定函数并返回结果列表。请注意,参数首先具有函数,而不是列表;这很自然,你很快就会看到。
我们希望将这两者放在一起healthyPeople
:
healthyPeople :: PeopleStats -> [String]
healthyPeople sts = map (\(n,_,_) -> n) $ filter (\(_,h,w) -> healthy h w) sts
这就是你想要的。 $
是函数应用程序,但由于其优先级,有效地将右侧分组;这让我们可以省略括号。在这里,我们看到为什么让map
首先使用它的功能是件好事。我们传递名称提取函数((n,_,_)
是一个模式,它将匹配三元组并分配n
其第一个元素,忽略其他两个元素),然后(通过$
)过滤清单。
这很好,但不是我真正写它的方式。由于sts
是函数及其主体的最后一个参数,因此不需要。事实上,Haskell中的所有函数只需要一个参数;这意味着如果你没有传递足够的参数,你会得到一个函数,它需要缺少的参数并返回结果。在函数组合运算符.
的帮助下,这给了我们
healthyPeople :: PeopleStats -> [String]
healthyPeople = map (\(n,_,_) -> n) . filter (\(_,h,w) -> healthy h w)
这可能就是我写它的方式!您会发现自己经常使用map
和filter
;它们是函数式编程中真正的主力。
还有另一种惯用的方式可以写healthyPeople
;您可以使用列表推导,如下所示:
healthyPeople :: PeopleStats -> [String]
healthyPeople stats = [n | (n,h,w) <- stats, healthy h w]
这读作“构建每个n
的列表,(n,h,w)
是stats
的元素,healthy h w
为真。如果任何模式匹配或者谓词失败(你可以有不止一个,虽然你不需要这个),跳过该元素;否则,|
的左侧被执行。这实际上是写{的另一种方式{1}} / map
版本。
编辑1:正如其他许多人所说,您的单位已于filter
关闭;你应该有bmi
。此外,您的heightCm/100
函数的代码相当于
healthy
这相当于以类似C语言写作
f x | cond = True
| otherwise = False
相反,你应该写
bool f(some_type x) {
if (cond)
return true;
else
return false;
}
或者,在这种情况下
bool f(some_type x) {
return cond;
}
这为您提供了更短的代码
f x = cond
(您也可以使用healthy :: Height -> Weight -> Bool
healthy heightCm weightKg = let index = bmi heightCm weightKg
in 25 > index && 18 < index
条款,但这里只是where
,因为我更喜欢它:))
答案 4 :(得分:0)
type Height = Float
type Weight = Float
data PersonStats = PersonStats
{ personName :: String, personHeight :: Height, personWeight :: Weight }
bmi :: Height -> Weight -> Float
bmi heightCm weightKg = weightKg/(heightCm / 100)^2
healthy :: Height -> Weight -> Bool
healthy heightCm weightKg = 25 > index && 18 < index
where index = bmi heightCm weightKg
healthyPerson :: PersonStats -> Bool
healthyPerson p = healthy (personHeight p) (personWeight p)
healthyPeople :: [PersonStats] -> [String]
healthyPeople = map personName . filter healthyPerson