Haskell语法与函数,特别是findMin函数

问题描述:

我正在努力与Haskell的语法。这很简单,但我卡住了。Haskell语法与函数,特别是findMin函数

我想写一个findMin函数,它需要一个列表并找到最小值。这是我的代码,我尝试了很多语法的东西,我可以获得任何帮助。

findMin [] = [0] 
findMin list = if any < head list then findMin(tail) else take 1 

而我得到各种类型的错误。什么问题?

(如果它有助于在所有我在面向对象编程背景)

+2

什么是“任何”?这不是一个功能吗?与功能比较没有多大意义。另外,有什么错误? – Carcigenicate

+1

最后的'拿1'意味着什么?这会导致类型错误,因为你只是部分应用'take'。我想提供帮助,但我不想只为您写功能。您将需要缩小问题范围,因为此代码存在一些问题。 – Carcigenicate

+0

我看到“any”是你可以用来表示列表中的任何其他元素的东西。 – Cheyanne

我看到你有东西在评论想通了,但我会添加一些东西在这里希望能够提供帮助。我也觉得我应该很快提到,Haskell已经有一个minimum函数,以防万一任何人不情愿地试图学习这门语言,并且实际上需要某种功能。

首先我们来谈谈类型。我通常会想到findMin风格的函数返回一个列表内的最低值,而不是值,因此类型将是: 前findMin :: (Num a, Ord a) => [a] -> a

的事情=>添加上下文的功能类型。这限制了所有a只能是有秩序的东西(否则我们怎么能找到最小值)。其次Num a势力a是一个数字,这是必要的,因为你指定空列表的情况应该是0.

我会解释2种其他方式来编写findMin函数,试图使它们更简洁你的定义(Haskell的好处之一是它可以是多么的简洁,我也发现它有助于学习看到多种可能性)。第一个将使用递归和第二个守卫将使用列表理解。

我们不能用findMin [] = 0做很多事情,所以我们会进入带有东西的列表。

我们需要小心的递归定义,因为最终我们将评估,一定可以得到0,所以我们需要通过定义一个值的情况下,停止之前的递归: findMin [x] = x

当传递列表作为一个函数的参数,你可以分离出它的元素并给它们分别命名,所以(x:xs)意味着一个值x是第一个元素后跟元素列表xs

对于这个定义,我们将定义自己的前两个元素,然后将其余元素:

findMin (x:y:xs) 
    | x < y = findMin (x:xs) 
    | otherwise = findMin (y:xs) 

卫兵让我们有根据条件的函数的多个定义。如果x < y我们想摆脱y,因为它不能是最小值,所以我们找到最小值x和其余元素xs。如果x不小于y那么最小值是yxs中的一个值。

定义此功能的第二种方法是使用列表理解(这是我最喜欢的,因为它特别简洁)。

我们不使用递归,所以我们不需要为一个元素的情况下,我们可以保持我们定义一个空列表,并直接与元素的任何名单:

findMin xs = head [x | x <- xs, all (>= x) xs]

那么,什么是去这里? [x | x <- xs]创建了一个x值的列表,其中x是来自xs的所有元素。然后,我们添加一个条件来说,如果all (>= x) xs意味着如果xs的所有元素大于或等于xs,我们只需要这些值。

这会产生一个最小元素列表。如果最小出现一次,这可能有一个元素,或者如果出现多次,可能有几个元素。无论哪种方式,他们都是一样的,所以我们只是采取第一个使用head

希望这会有所帮助,并希望你有兴趣学习Haskell。随意问,如果您有任何疑问:)

+2

+1,但请不要推荐列表理解解决方案。这不是惯用的 - 头部被拒绝是因为部分......尽管实际上你可以通过保留原始的' - > [a]'签​​名并用'take 1'取代它 - 更重要的是,它效率非常低:你将每个元素与_all_其他元素进行比较,即它具有_O_(_n_²)复杂度而不是_O_(_n_)。 – leftaroundabout

+0

@leftaroundabout我很欣赏反馈。如果你没有注意到,在使用'head'的定义之前,我提到保留空列表的情况,因此据我所知,头部在这里失败是没有可能的。无论使用它都不好吗? 关于复杂性,我认识到它很糟糕,但这不是真正的答案。我更多地考虑了你可以考虑这个问题的不同方式。如果效率是相关的,这不仅仅是学习,那么没有理由不使用已有的“最小化”功能。 –

+0

谢谢James,这对于解释Haskell的一些基础知识非常有帮助。我很欣赏你花时间表现的时间。 – Cheyanne

在ghci的这个功能似乎做你的绝招:

let findMin x = if length x > 1 then min (head x) (findMin (tail x)) else head x 

我学习,我试着回答这里的一些问题,所以任何反馈会赞赏。

+1

嗯...我不认为所以答案应该要求反馈,但是......在这里你去:** 1。**不要使用'length'。这非常昂贵(需要遍历整个列表)。要检查列表是否为空,请改用['null'](http://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Foldable.html#v:null)。但更好的是在'findMin'的子句中匹配模式,也就是单独使用'findMin [] = ...'和'findMin(x:xs)= ...'** 2。 ...然后... else'。即使布尔检查不能用模式匹配来替代,警卫通常比“如果”更好看。 – leftaroundabout

+1

** 3.不要使用'head'和'tail',这些都是部分函数(不能在空列表上工作),即很容易犯错误,这会导致运行时错误。如果你使用了模式匹配'findMin(x:xs)= ...',这将不是问题,因为那么头部/尾部部分根本就不在与空列表匹配的地方。 ** 4.使用[尾递归](https://en.wikipedia.org/wiki/Tail_call)实现''findMin'实际上效率更高,但这并不重要。 – leftaroundabout