在 Haskell 的列表中计算“完美正方形”

时间:2021-04-09 16:07:48

标签: haskell

我是 Haskell 的新手,我正在努力让这个概念发挥作用。

我必须使用整数列表作为参数并计算列表中完全平方数(1、4、9 等)的数量并输出该值。例如,如果我输入 myfunction[1,5,9],输出将是 2。

到目前为止,我只有一个函数可以工作:

SELECT *
FROM some_table
WHERE date_time_column BETWEEN ? AND ?

将元素的平方根乘以元素的平方根。我的心态是我可以将上述结果设置为与原始元素相等。如果它们彼此相等,那就意味着它们是完美的正方形。然后我会增加一个值。

我的问题是我的书没有给我任何关于如何增加变量或如何一次合并多个函数的想法。结果,我在 3 天的时间里一直在漫无目的地工作。

预先感谢您提供任何帮助、建议或资源!

1 个答案:

答案 0 :(得分:2)

修复您的版本

首先完成您的版本可能如下所示:

myfunction list = length $ filter per list
    where 
        per y = floor(sqrt y * sqrt y) == y

这甚至可以进行类型检查,但它不起作用(尝试一下)

那是因为有一个小问题 - 让我们通过提供一些类型来使其显而易见:

myfunction :: [Int] -> Int
myfunction list = length $ filter per list
    where 
        per :: Int -> Bool
        per y = floor(sqrt y * sqrt y) == y

你得到一个错误:

No instance for (Floating Int) arising from a use of ‘sqrt’

它试图告诉您它不知道如何将 sqrt 用于 Int - 一个简单的解决方法是使用 fromIntegral 并让它转换 {{1}进入可以:

Int

这种工作(错误答案)但会发出警告 - 我们可以摆脱

myfunction :: [Int] -> Int
myfunction list = length $ filter per list
    where 
        per :: Int -> Bool
        per y = floor(sqrt (fromIntegral y) * sqrt (fromIntegral y)) == y

我们是否告诉 Haskell 使用哪种类型进行转换(警告会告诉您无论如何您都默认使用此类型)。

所以还是有错误的答案。

@jpmarinier 已经说明了原因——很遗憾,你测试/myfunction :: [Int] -> Int myfunction list = length $ filter per list where per :: Int -> Bool per y = floor(sqrt (fromIntegral y :: Double) * sqrt (fromIntegral y)) == y 的方式并没有削减它(至少我认为你想要)——这里有一个修复:

sqr

我们首先将 myfunction :: [Int] -> Int myfunction list = length $ filter per list where per :: Int -> Bool per y = let y' = (fromIntegral y :: Double) in sqrt y' ** 2 == y' 转换为 yDouble 并对其进行测试。

另一种选择是使用 integer-sqr 作为@jpmarinier 提到的:

y'

这应该终于奏效了。

没有myfunction :: [Int] -> Int myfunction list = length $ filter per list where per :: Int -> Bool per y = squareRoot y * squareRoot y == y squareRoot :: Int -> Int squareRoot = floor . sqrt . (fromIntegral :: Int -> Double) floor

好吧,这对您来说可能有点难理解,但您可以通过以下方法筛选值来做到这一点。

让我们从创建所有完美平方的(升序)列表开始 - 我不知道您希望这些数字是哪种类型,所以我们也保持通用:

sqr

不,让我们创建一个函数来计算两个列表中的元素 - 如果列表已排序,则可以通过沿着列表递归地完成 - 我不知道您的输入列表是否始终已排序,所以让我们先对其进行排序:

-- need later
import Data.List (sort)

perfectSquares :: Enum a => Num a => [a]
perfectSquares = [ n*n | n <- [1..] ]

我们可以将两者结合起来得到答案:

countOccurances :: (Enum a, Num a, Ord a) => [a] -> [a] -> a
countOccurances from list =
    countAlong from $ sort list
    where
        countAlong [] _ = 0
        countAlong _ [] = 0
        countAlong allXs@(x:xs) allYs@(y:ys)
            | x < y = countAlong xs allYs
            | x > y = countAlong allXs ys
            | otherwise = 1 + countAlong allXs ys

示例:

import Data.List (sort)

countPerfectSquares :: (Enum a, Num a, Ord a) => [a] -> a
countPerfectSquares = countOccurances perfectSquares

countOccurances :: (Enum a, Num a, Ord a) => [a] -> [a] -> a
countOccurances from list =
    countAlong from $ sort list
    where
        countAlong [] _ = 0
        countAlong _ [] = 0
        countAlong allXs@(x:xs) allYs@(y:ys)
            | x < y = countAlong xs allYs
            | x > y = countAlong allXs ys
            | otherwise = 1 + countAlong allXs ys

perfectSquares :: Enum a => Num a => [a]
perfectSquares = [ n*n | n <- [1..] ]