【问题标题】:Haskell-Project Euler 4Haskell-Project Euler 4
【发布时间】:2023-03-21 09:35:02
【问题描述】:

我对 Haskell 很陌生,我认为要掌握编写 Haskell 程序的窍门,我可能会解决一些项目的欧拉问题。

所以我继续它并实现了 Project Euler 的第 4 个问题。

问题陈述:

回文数的两种读法相同。由两个 2 位数乘积构成的最大回文数是 9009 = 91 × 99。

找出由两个 3 位数乘积构成的最大回文数。

但我的解决方案似乎有问题。

这里是:

projectEuler4 :: (Ord a,Num a) => a 

projectEuler4 = max palindromeList
  where palindromeList = [reverse(x*y)|x<-[1..999],y <- [1..999]]

GHCI 给我这个错误:

ProjectEuler4.hs:2:17:
Could not deduce (a ~ ([[a0]] -> [[a0]]))
from the context (Ord a, Num a)
  bound by the type signature for
             projectEuler4 :: (Ord a, Num a) => a
  at ProjectEuler4.hs:1:18-35
  `a' is a rigid type variable bound by
      the type signature for projectEuler4 :: (Ord a, Num a) => a
      at ProjectEuler4.hs:1:18
In the return type of a call of `max'
Probable cause: `max' is applied to too few arguments
In the expression: max palindromeList
In an equation for `projectEuler4':
    projectEuler4
      = max palindromeList
      where
          palindromeList
            = [reverse (x * y) | x <- [1 .. 1000], y <- [1 .. 1000]]

我不知道这意味着什么,并且因为找不到错误原因而感到沮丧。 任何帮助将不胜感激。谢谢。


所以在阅读了一些答案和cmets之后, 我做了这样的事情:

projectEuler4 :: (Ord a,Num a) => a 
projectEuler4 = max' palindromeList
    where palindromeList = [reverse(show(x*y))|x<-[1..999],y <- [1..999]]

max' :: (Ord a) => [a] -> a
max' [] = error "Empty List"
max' [p] = p
max' (p:ps) = max p (max' ps)   

还是不行。


好的..

按照@bheklilr 的建议,我改变了我的程序:

products :: [Integer] -> [Integer] -> [Integer]
products ns ms = [x * y | x <- ns, y <- ms]

isPalindrome :: Integer -> Bool
isPalindrome n = let s = show n in s == reverse s

palindromes :: [Integer]
palindromes = maximum filter (isPalindrome "") (products [100..999] [100..999])

现在我用什么代替引号?我很困惑。

【问题讨论】:

  • 这可能与您编写max(palindromeList) 的方式有关。试试max palindromList,不带括号。 Haskell 不使用 C 风格的调用约定。
  • @Eric 这两个表达式是等价的。括号用于分组,写(((5))) + 4 :)
  • 那么类型类有什么问题吗?
  • 你需要一个 Haskell 教程才能开始。 max 适用于两个数字,而不是列表(尽管也适用于列表)。您可以使用 GHCi :t max 或使用 Hoogle 找到此类信息。
  • @WillNess 我认为函数和参数之间没有空格会导致问题,但我认为没有。今天我学会了……

标签: haskell


【解决方案1】:
Prelude> :t max
max :: (Ord a) => a -> a -> a

这是max 的类型。当你在a 类型的某个参数上调用它时,你会得到a -&gt; a 类型的结果——一个函数。那是因为max 通常用两个值调用;部分应用导致函数在计算结果之前仍等待第二个参数,即两个值中的最大值。

错误表明 Haskell 已将palindromeList 的类型推导出为[[a0]],因此结果的类型为[[a0]] -&gt; [[a0]]。你给它(Ord a,Num a) =&gt; a,而Haskell无法匹配这两者。

您打算使用maximum,它处理一个列表并在其中找到最大的值:

Prelude> :t maximum
maximum :: (Ord a) => [a] -> a

palindromeList 的定义也是错误的。首先,[1..999] 中从 1 到 99 的数字不是三位数。然后你需要测试它们。 reverse (x*y) 当然是错的:reverse :: [a] -&gt; [a] 但是两个数字相乘的结果是一个数字,但是 - 即使你修复它,这仍然不是一个测试。

测试类似于show (x*y) == reverse (show (x*y))

【讨论】:

    【解决方案2】:

    第一个大错误是你正在调用

    reverse (x * y)
    

    由于x * y 是一个数字,而reverse 仅适用于列表,因此无法编译。您可以使用show 将数字转换为String(这是一个列表):

    reverse $ show $ x * y
    

    但是,反转字符串并不是您真正想要做的,您想要过滤查找所有回文,因此您需要通过谓词过滤您的(x * y)s 列表。相反,你可以写

    palindromeList = [z | x <- [1..999], y <- [1..999], let z = x * y, if show z == reverse (show z)]
    

    但由于这超出了屏幕的一侧,我建议将其分解为更小的函数

    -- Generates all products
    products :: [Integer] -> [Integer] -> [Integer]
    products ns ms = [x * y | x <- ns, y <- ms]
    
    -- Checks if a number is a palindrome
    isPalindrome :: Integer -> Bool
    isPalindrome n = let s = show n in s == reverse s
    
    -- Generates problem-specific palindromes
    palindromes :: [Integer]
    palindromes = ??? -- Implementation here.  Hint: filter
    

    下一个大问题是因为您使用的是max 函数,它的类型为

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

    但是我们真的很想找到一个列表的最大值,所以我们转向maximum,它的类型是

    maximum :: Ord a => [a] -> a
    

    这样你就可以完成你的程序了

    projectEuler4 :: Integer
    projectEuler4 = maximum palindromes
    

    最后一个想法:问题是您需要找到最大的回文数,它是 2 个三位数的倍数,但您正在查看的范围是 [1..999],其中包括 1 位和 2 位数字.你能做些什么来不检查这些?方便的是,它会使程序更快。

    【讨论】:

      【解决方案3】:

      您正在尝试使用以下方式反转数字:

      reverse(x*y)
      

      reverse 仅适用于列表。幸运的是,String 是一个列表,show 是创建值的 String 表示的规范方法。

      所以尝试一下:

      reverse (show (x*y))
      

      【讨论】:

      • true,但不是给出错误消息的原因。当它被修复时,它将保持(相似)。 :)
      • 是的..消息没有改变。
      【解决方案4】:

      你被要求找出两个三位数的最大乘积是回文数。该程序将生成一个回文数列表并找到该列表的最大值。第一步是生成产品的术语,称它们为 xy。这是使用生成器x &lt;- [100..999]y &lt;- [x..999] 完成的。请注意,如果我们正在考虑 x 的特定值,我们只需要考虑大于或等于 xy 值;如果没有,我们重复一遍。接下来,我们需要确定 xy 是否为回文。实现这一点最简单的方法是将xy 转换为字符串,将其命名为s,然后检查reverse s == s 是否成立。为此,请使用show :: Show a = a -&gt; String。生成列表的最大值可以通过函数maximum :: Ord a =&gt; [a] -&gt; a找到。

      euler4 :: Integral a => a
      euler4 = maximum [ x * y | x <- [100..999], y <- [x..999], palindromic (x * y) ]
        where palindromic n = show n == reverse (show n)
      

      您的代码存在几个问题。首先,projectEuler4 应该是一个整数值,并且所有整数值都已经是OrdNum 的实例。正确的约束是Integral a =&gt; a。这将提供IntInteger 的选择。第二,max :: Ord a =&gt; a -&gt; a -&gt; a 给出了Ord 类型类的两个成员的最大值,而不是列表元素的最大值 第三,reverse :: [a] -&gt; [a] 对列表进行操作,而不是数值;您需要先将x * y 转换为String,然后再将其应用于reverse。第四,y &lt;- [999] 表示y 是从仅包含999 的单例列表生成的,而不是从x999100999 的数字列表。

      haskell.org 的 Haskell 教程对列表推导有很好的概述。我还会查看 Haskell 类型系统的概述,并学习使用交互式 GHC 环境——尤其是命令 :t(如果您不确定函数的类型并且不熟悉标准库)。

      【讨论】:

        猜你喜欢
        • 2015-04-16
        • 2012-07-09
        • 1970-01-01
        • 2011-03-23
        • 1970-01-01
        • 2023-03-15
        • 2013-12-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多