【问题标题】:how to correctly apply the foldr function如何正确应用 foldr 功能
【发布时间】:2019-02-05 14:34:30
【问题描述】:

我试图找到一个列表的最大元素,其中元素是我自己创建的数据类型,使用折叠而不是递归进行。但是我最终得到一个错误“无法匹配类型”。由于我对 haskell 相当陌生,我无法看到问题并希望知道如何正确应用 foldr 功能。

我获取最大元素的尝试如下所示:

--(foldr比较(头列表)(尾列表))

不会编译。

我已经包含了 Eq 和 Ord 的数据类型及其实例,我还包含了比较函数。

 -- Car data type

 data Car = Car {registration :: String,
                    hour :: Integer, 
                  minute :: Integer, 
               tupleForm :: PTime
           }

-- Equality comparison between Car data types
-- reqiures equality by registration number, hours and minutes

instance Eq Car where 
     (Car r1 h1 m1 _) == (Car r2 h2 m2 _) = (r1 == r2) && (((h1*60)+m1) == 
((h2*60)+m2))

-- Order comparison between Car data types
-- Compares time of two Cars , hours and minutes 

instance Ord Car where 
     compare (Car _ h1 m1 _) (Car _ h2 m2 _) = compare ((h1*60)+m1) 
     ((h2*60)+m2)  


-- Returns the larger Car
comparison :: (Car,Car) -> Car
comparison(car1,car2) = if(car1 > car2) then car1 else car2

折叠Car列表后我的预期结果是获得“最大的汽车”,这基本上是指时间最长的汽车。但是由于类型错误,我最终会出现编译错误。

【问题讨论】:

  • 一旦你将一个类型设为Ord 的实例,就像你在这里一样,你会“免费”获得一个maximum 函数。所以你不需要实现你自己的。 (如果你想练习,你可能应该使用foldr1 而不仅仅是foldr,因为找到一个空列表的最大值没有多大意义)。
  • 没想到最大功能,多好啊!我将使用它而不是折叠,但我将把这个问题留给未来的参考,以供将来参考如何正确折叠这样的东西,看看我是如何无法弄清楚什么时候出错了。非常感谢@RobinZigmond!

标签: haskell syntax fold


【解决方案1】:

问题是comparison的类型和定义。

首先,类型应该是Car -> Car -> Car:您取两个 Car 值并返回较大的值。

其次,您对comparison 的定义尝试将一个单个 参数匹配为一个元组,而不是两个单独的参数。去掉括号和逗号。

comparison :: Car -> Car -> Car
comparison car1 car2 = if car1 > car2 then car1 else car2

(当然,comparison 只是 max 仅限于 Car 而不是 Ord a => a

comparison :: Car -> Car -> Car
comparison = max

而且,正如 Robin Zigmond 指出的那样,foldr comparison x 基本上是 maximum 以获得适当的值 x。)

【讨论】:

  • 我一定上传了错误的比较版本,因为我最初传递了一对 Car 作为参数。但是看到您的回答,我明白这不是它想要的元组对。谢谢!
  • 您可以定义comparison' :: (Car, Car) -> Carcomparison' (car1, car2) = <same definition as before>,但这不是惯用的。 (不过,两者是同构的,如comparison == curry comparison'comparison' == uncurry comparison。)
  • 只是为了澄清-我指的是maximum,指的是OP似乎试图用foldr实现的内容,以根据定义的顺序获得列表的最大值。当然你指出这里显示的comparison 只是uncurry max 是对的
  • 啊,好的。我假设您犯了与我通常犯的相同的错误,即混淆了 maximummax 中的哪一个采用列表(呃,Foldable),哪个采用两个显式值。 :)
  • @RobinZigmond 我已经编辑了我的答案,试图在你的嘴里说出更准确的词:)
【解决方案2】:

考虑foldr的简化类型。

foldr :: (a -> b -> b) -> b -> [a] -> b

这比您要求的要笼统得多,因为您要处理所有汽车。这意味着找到最大的Carfoldr,类型变为

foldr :: (Car -> Car -> Car) -> Car -> [Car] -> Car

第一个参数是一个在两辆汽车之间进行选择的函数。在你的情况下,你想要 max 因为它的类型

max :: Ord a => a -> a -> a

变成

max :: Car -> Car -> Car

并且完全匹配。

foldr 的第二个参数命名为 z,表示零。它是折叠过程的种子。为此,您也可以使用列表的第一个元素,通过head 获得。

[Car] 类型的列表参数显然是您要计算其最大值的列表。您可以传递整个列表,但头部已被视为 z 参数。最好是tail list

给定以下列表(在修改Car 以删除tupleForm 并派生Show 实例之后)

cars = [ Car "A" 1 2, Car "B" 3 4, Car "C" 10 10 ]

foldr 找到最大值

λ> foldr max (head cars) (tail cars)
Car {registration = "C", hour = 10, minute = 10}

请注意,foldr 的这个应用程序等同于 maximum,但您不必相信我的话。添加

import Test.QuickCheck

到源文件的顶部,然后

prop_max :: [Car] -> Property
prop_max l =
  not (null l) ==>
    maximum l == foldr max (head l) (tail l)

instance Arbitrary Car where
  arbitrary = do
    r <- oneof $ map return ["Apple","Orange","Banana"]
    h <- choose (0,23)
    m <- choose (0,59)
    return (Car r h m)

使断言更有信心。

λ> quickCheck prop_max
+++ OK, passed 100 tests.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-24
    • 2019-11-16
    • 1970-01-01
    • 1970-01-01
    • 2021-04-15
    • 2020-01-05
    • 2019-09-28
    • 1970-01-01
    相关资源
    最近更新 更多