【问题标题】:Haskell How to find an infinite loop?Haskell 如何找到无限循环?
【发布时间】:2018-02-26 23:18:57
【问题描述】:

我是 haskell 的初学者,因此我很难调试,因为它与命令式语言非常不同。当我尝试运行这段代码时,我得到了一个无限的Int 列表,但我不知道它为什么是无限的。现在我知道代码可能很丑陋而且效率不高,但我的目标只是让它工作,而不是让它高效工作。

chaineVersSon :: String -> Int -> [Int]
chaineVersSon chaineAInterpreter battementParMinute = integriser (concat 
(musicaliser (freqNotes (interpreter 9 4 1 5 chaineAInterpreter [])) 
battementParMinute))

integriser :: [Double] -> [Int]
integriser [] = []
integriser (note:notes) = (floor (note * 32767)):integriser notes

musicaliser :: [(Double, (Double, Double))] -> Int -> [[Double]]
musicaliser [] _ = []
musicaliser (note:notes) tempo = (creerSon note tempo 0):musicaliser notes tempo

creerSon :: (Double, (Double, Double)) -> Int -> Double -> [Double]
creerSon note tempo temps
    | temps < (calcDuree note tempo temps) = (echantillonner note 
temps):creerSon note tempo (temps + (1/(fromIntegral frequenceEchantillonage)))
    | temps == (calcDuree note tempo temps) = (echantillonner note temps):[]

calcDuree :: (Double, (Double, Double)) -> Int -> Double -> Double
calcDuree note tempo temps = (60 * (fst(snd note)) / (fromIntegral tempo)) / 
(1/(fromIntegral frequenceEchantillonage)) + temps

echantillonner :: (Double, (Double, Double)) -> Double -> Double
echantillonner note temps = ((snd (snd note)) / 10) * sin(2 * pi * (fst     note) * temps)

--Fonction qui traite toutes les modifications d'état
interpreter :: Int -> Int -> Double -> Double -> String -> [((Int, Int), 
(Double, Double))] -> [((Int, Int), (Double, Double))]
interpreter _ _ _ _ [] _ = []
interpreter note octave duree volume (etat:chaine) pile
    | etat == '0' = interpreter note octave duree 0 chaine pile
    | etat == '.' = interpreter note octave (duree*1.5) volume chaine pile
    | etat == '/' = interpreter note octave (duree/2) volume chaine pile
    | etat == '[' = interpreter note octave duree volume chaine (((note, octave), (duree, volume)):pile)
    | etat == ']' = interpreter (fst (fst save)) (snd (fst save)) (fst (snd save)) (snd (snd save)) chaine pile
    | etat >= 'a' && etat <= 'g' = interpreter (changerNote etat) octave duree volume chaine pile
    | etat >= '2' && etat <= '9' = interpreter note (read [etat]) duree volume chaine pile
    | etat == '&' = interpreter (fst (diminuer note octave)) (snd (diminuer note octave)) duree volume chaine pile
    | etat == '&' = interpreter (fst (augmenter note octave)) (snd (augmenter note octave)) duree volume chaine pile
    | etat == '!' = ((note, octave), (duree, volume)):(interpreter note octave duree volume chaine pile)
    | otherwise = interpreter note octave duree volume chaine pile
                      where save = head pile

--Fonction pour changer la note selon l'option fournie
changerNote :: Char -> Int
changerNote etat
    | etat == 'a' = 9
    | etat == 'b' = 11
    | etat == 'c' = 0
    | etat == 'd' = 2
    | etat == 'e' = 4
    | etat == 'f' = 5
    | etat == 'g' = 7

--Fonction qui diminue la note et l'octave si possible/nécéssaire
diminuer :: Int -> Int -> (Int, Int)
diminuer note octave
    | note > 0 = (note - 1, octave)
    | note == 0 && octave == 2 = (note, octave)
    | otherwise = (11, octave - 1)

--Fonction qui augmente la note et l'octave si possible/nécéssaire
augmenter :: Int -> Int -> (Int, Int)
augmenter note octave
    | note < 11 = (note + 1, octave)
    | note == 11 && octave == 9 = (note, octave)
    | otherwise = (0, octave + 1)

--Fonction récursive pour calculer la fréquence de chaque note à jouer
freqNotes :: [((Int, Int), (Double, Double))] -> [(Double, (Double, Double))]
freqNotes [] = []
freqNotes (note:notes) = ((calcFrequence (fromIntegral (fst (fst note)))     (fromIntegral (snd (fst note)))), (fst (snd note), snd (snd note))):freqNotes     notes

--Fonction qui calcule la fréquence selon la note et l'octave
calcFrequence :: Double -> Double -> Double
calcFrequence note octave = 440 * (2**(1/12))**((12 * octave) + note - 57)

main::IO()
main = do argv <- getArgs
          chaine <- readFile ( argv !! 0 )
          print "Begin..."
          print ( chaineVersSon chaine ( ( read ( argv !! 1 ) ) :: Int ) )
          print "Done!"

通常在其他语言中,我只会在任何地方打印内容并查看结果出错的地方,但我不知道如何在 Haskell 中做到这一点,而无需重写几乎整个代码。我应该使用什么样的方法来查找我的代码出错的地方?

我已经阅读过有关跟踪的信息,但我似乎无法用它实际打印任何内容。

【问题讨论】:

标签: haskell


【解决方案1】:

如果您的列表是无限的,这意味着creerSon 总是转到第一种情况,这意味着temps 总是小于calcDuree note tempo temps,这是因为calcDuree 在末尾有+ temps

我注意到这一点是通过重构您的代码,使用诸如map 之类的库函数使其更短,直到我能够阅读为止。

您可以通过在您的程序上运行代码覆盖率工具(例如 stack test --coverage)来查看曾经到达的代码,从而注意到这一点。

【讨论】:

    猜你喜欢
    • 2016-01-27
    • 1970-01-01
    • 2013-10-05
    • 2013-12-24
    • 2011-02-23
    • 1970-01-01
    • 1970-01-01
    • 2014-07-09
    • 2013-04-12
    相关资源
    最近更新 更多