Haskell的if语句用于错误检查

时间:2010-09-28 00:52:30

标签: haskell

我为Haar小波变换编写了一个函数,因为输入是一个功率为2的List。我试图通过确保在执行转换之前List的长度是2的幂来进行错误检查。我正在比较列表长度的日志基数2,看它是否均匀出现(小数点右边没有)。我认为haskell中的if语句有一些问题我不熟悉其他语言。如果我不进行错误检查并且只是使用正确的参数调用haar,它就能完美地工作。

haar :: (Fractional a) => [a] -> [a]
haar xs = if logBase 2 (fromIntegral (length xs)) /= truncate (logBase 2 (fromIntegral (length xs)))
             then error "The List must be a power of 2"
             else haarHelper xs (logBase 2 (fromIntegral (length xs)))

haarHelper xs 1 = haarAvgs xs ++ haarDiffs xs
haarHelper xs level = haarHelper (haarAvgs xs ++ haarDiffs xs) (level - 1)

haarAvgs [] = []
haarAvgs (x:y:xs) = ((x + y) / 2.0) : haarAvgs xs 

haarDiffs [] = []
haarDiffs (x:y:xs) = ((x - y) / 2.0) : haarDiffs xs

我收到以下错误消息:

functions.hs:52:13:
    Ambiguous type variable `t' in the constraints:
      `Floating t'
        arising from a use of `logBase' at functions.hs:52:13-48
      `Integral t'
        arising from a use of `truncate' at functions.hs:52:53-99
    Probable fix: add a type signature that fixes these type variable(s)
Failed, modules loaded: none.

5 个答案:

答案 0 :(得分:4)

有一个更简单,更快速的实现来检查正整数是2的幂:

import Data.Bits

powerOfTwo' n = n .&. (n-1) == 0

(注意:这省略了n为正的检查,假设我们可以依赖length

解释,好奇:

该算法依赖于唯一属性,即只有2的幂才有一个1位(根据定义),并且递减它们会反转所有低位:

2^n      =  100000...
2^n - 1  =  011111...

这没有共同的位,使它们按位和零。

对于所有非幂的2,递减将至少保持最高1位不变,保持按位和结果非零。

(维基百科:Fast algorithm to check if a positive number is a power of two

答案 1 :(得分:2)

haar :: (Fractional a) => [a] -> [a]
haar xs | r /= (truncate r) = error "The List must be a power of 2"
        | otherwise = haarHelper xs (logBase 2 (fromIntegral (length xs)))
            where r = logBase 2 (fromIntegral (length xs))

是的,看起来像截断的东西。如果使用haskell的语句如上所示,则更容易编写。可能会帮助调试一下。

我想我可能知道。我认为truncate返回一个int,其他数字是浮点数。

试试这个

haar :: (Fractional a) => [a] -> [a]
haar xs | r /= w = error "The List must be a power of 2"
        | otherwise = haarHelper xs (logBase 2 (fromIntegral (length xs)))
            where 
                r = logBase 2 (fromIntegral (length xs))
                w = intToFloat (truncate r) 

haarHelper xs 1 = haarAvgs xs ++ haarDiffs xs
haarHelper xs level = haarHelper (haarAvgs xs ++ haarDiffs xs) (level - 1)

haarAvgs [] = []
haarAvgs (x:y:xs) = ((x + y) / 2.0) : haarAvgs xs 

haarDiffs [] = []
haarDiffs (x:y:xs) = ((x - y) / 2.0) : haarDiffs xs

intToFloat :: Int -> Float
intToFloat n = fromInteger (toInteger n)

答案 2 :(得分:1)

补充马特的答案:

haar :: (Fractional a) => [a] -> [a]
haar xs | r /= (fromIntegral $ truncate r) = error "The List must be a power of 2"
        | otherwise = haarHelper xs r
            where r = logBase 2 (fromIntegral (length xs))
  1. 使用fromIntegral
  2. 转换截断的积分结果
  3. 您可以在r
  4. 中使用haarHelper xs r的定义

答案 3 :(得分:0)

这是haar的一个版本,我认为它会更好一些:

haar :: (Fractional a) => [a] -> [a]
haar xs = maybe (error "The List must be a power of 2")
                (haarHelper xs)
                (intLogBase 2 $ length xs)

intLogBase :: (Integral a) => a -> a -> Maybe Int
intLogBase b n = intLogBase' b 1 n
  where
    intLogBase' b p 1 = Just p
    intLogBase' b p n
      | r /= 0    = Nothing
      | otherwise = intLogBase' b (p + 1) q 
      where (q, r) = quotRem n b

intBaseLogbaseLog的变体,适用于整数,如果给定的数字不是给定基数的幂,则返回Nothing。否则它将返回Just中包含的权力。与logBasetruncate不同,没有转换为浮点数:我们一直有整数。

哈尔的maybe function有三个论点。它将评估它的最后一个参数,如果它是Nothing,它将返回第一个参数(在这种情况下是错误)。如果最后一个参数的计算结果为Just,它会将第二个参数应用于Just内的事物并返回该参数。

答案 4 :(得分:0)

这里的问题与if表达无关。正如在其他答案中所提到的那样,你将“logBase(...)”与“truncate(logBase(...))”进行比较。一个返回一个Floating类型,另一个返回一个Integral类型。没有类型(在标准库中)实现这两个类,因此条件不能按原样进行良好类型化。

我在使用2的幂时偶尔使用的模式是保留2的幂列表,并检查该数字是否在该列表中。例如:

powersOfTwo :: [Integer]
powersOfTwo = iterate (*2) 1
isPowerOfTwo x = xInt == head (dropWhile (<xInt) powersOfTwo)
    where xInt = toInteger x

我没有测试过,但我的直觉告诉我,对于大多数用途,这可能比“logBase 2”更快。即使不是,它也更合适,因为它不涉及任何浮点数学。特别是,即使修复了类型,你当前的方法也不会起作用:truncate (logBase 2 512) == truncate (logBase 2 550)(编辑:虽然我想我最初写这篇文章时可能会误解你的意图,但我现在意识到你可能想检查一下通过比较截断版本与非截断版本,而不是通过与任何已知值进行比较,logBase 2(...)是否是精确整数值。