2010-09-28 6 views
3

Я написал функцию для преобразования вейвлеров Haar, учитывая, что вход представляет собой список с мощностью 2. Я пытался проверить ошибку, убедившись, что длина списка является силой 2 перед формованием трансформации. Я сравниваю лог-базу 2 длины списка, чтобы увидеть, получится ли она равномерно (ничего справа от десятичной точки). Я думаю, что что-то происходит с утверждением if в haskell, что я не привык на других языках. Он отлично работает, если я не проверяю ошибки и просто вызываю хаар с правильным аргументом.Оператор Haskell для проверки ошибок

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. 
+0

Знаете ли вы, что эта реализация будет иметь крайне низкую производительность для списков какой-либо значительной длины? –

ответ

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, где другой номер является float.

Попробуйте

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) 
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. Вы можете использовать определение r в haarHelper xs r
+0

По-видимому, Мэтт отредактировал свой ответ, пока я печатал мой. – gawi

+0

Ваш выглядит лучше. Ха-ха. Но можете ли вы объяснить, что такое $. Все еще новенько для меня. Благодарю. – Matt

+0

Из отчета haskell98: $ имеет низкий приоритет право-ассоциативного привязки, поэтому он иногда позволяет исключить круглые скобки; например: f $ g $ h x = f (g (h x)) – gawi

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 

intBaseLog является разновидностью baseLog, которая работает для целых чисел и возвращает Nothing если указанное число не является степенью данности база. В противном случае он возвращает мощность, завернутую в Just. В отличие от logBase и truncate нет конверсии чисел с плавающей запятой: у нас есть целые числа.

maybe function в Haar принимает три аргумента. Он будет оценивать свой последний аргумент, и если это Nothing, он вернет первый аргумент (в данном случае ошибку). Если последний аргумент оценивается как Just, он применяет второй аргумент к предмету внутри Just и возвращает это.

+0

Это также можно изменить, чтобы исключить разделение и отдельный вызов длины. Основная стратегия, которую я имею в виду, это реализовать функцию dropExact :: Int -> [a] -> Maybe [a] 'и называть ее повторно с последовательными полномочиями 2 (начиная с дополнительного' 1'). В конечном итоге операция прекратится либо с помощью «Ничего» (с указанием недопустимой длины), либо «Just []» (с указанием успеха, длина которого равна следующей мощности 2, которая была бы удалена). – mokus

0

Проблема здесь не связана с выражением if. Как упоминалось в других ответах, это в вашем состоянии, когда вы сравниваете «logBase (...)» с «усечением» (logBase (...)) ». Один возвращает тип Floating, другой возвращает тип Integral. Нет типов (в стандартных библиотеках), которые реализуют оба класса, поэтому условие не может быть хорошо напечатано как есть.

Образец, который я иногда использую при работе с полномочиями двух, состоит в том, чтобы сохранить список полномочий двух и просто проверить, находится ли этот номер в этом списке. Например:

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) (Edit: хотя я думаю, что, вероятно, я неправильно понял ваше намерение, когда я написал это сначала, теперь я понимаю, что вы, вероятно, хотели проверить, является ли logBase 2 (...) является точным целочисленным значением, сравнивая усеченную версию с не усеченной версией, а не по сравнению с любым известным значением).

+0

Мне стало интересно узнать скорость этой стратегии против logBase, поэтому я бросил быстрый и грязный тест критерия. Для ввода, случайным образом распределенного от 0 до 2^31, эта стратегия примерно в два раза быстрее, чем использование logBase в моей системе. На практике это, как правило, будет быстрее, поскольку типичные списки будут намного короче, чем это, и эта реализация выполняется быстрее в более коротких списках. – mokus

4

Там это гораздо проще и быстрее реализации, чтобы проверить, что положительное целое число является степенью двойки:

import Data.Bits 

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

(Примечание: это не включает проверку, что n положительна, предполагая, что мы можем рассчитывать на это исходя из . length)

Объяснение, для любознательных:

Этот алгоритм основан на уникальной собственности, что только полномочия 2 имеют один 1 бит (по определению), и декремента их инвертирует все нижние бит:

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

Это не оставляет никаких общих черт, делая их побитовое и ноль.

Для всех не-сил-двух декремент оставит по крайней мере наивысший 1 бит без изменений, сохраняя побитовое и результат отличным от нуля.

(Википедия: Fast algorithm to check if a positive number is a power of two)