Я решил довольно простую задачу: создание всех убывающих последовательностей длиной L
, состоящих из натуральных чисел от 1
до M
в лексикографическом порядке.Тем не менее, я столкнулся с довольно странной проблемой.Взгляните:
c :: (Ord a, Num a, Enum a) => a -> a -> [[a]]
c m 1 = map return [1..m]
c m l = do
n <- [l..m]
res <- c (n - 1) (l - 1)
return $ n:res
c' :: (Ord a, Num a, Enum a) => a -> a -> [[a]]
c' m = helper 1 m where
helper :: (Ord a, Num a, Enum a) => a -> a -> a -> [[a]]
helper a b 1 = map return [a..b]
helper a b l = do
n <- [a..b]
True <- return $ (l - 1 <= n)
res <- helper a (n - 1) (l - 1)
return (n:res)
Итак, очевидно, что эти две функции делают абсолютно одно и то же (я проверял их на нескольких тестах, они обе дают правильные результаты на каждой), но если вы попытаетесь оценитьc 100 98
и c' 100 98
в GHCi, вы увидите огромную разницу во времени, которое требуется:
c 100 98: около 5 секунд ;
c '100 98: около 70 секунд ;
Как я уже упоминал, результат тот же .
Так что, мне немного неловкогенерируя [a..b]
каждый раз, но я немного расспросил, и было предположение, что Haskell не сопоставляет с образцом сразу, а задерживает его из-за ленивых вычислений, что вызывает огромное количестводополнительные звонки c'
.Однако вторая теория не совсем верна: я установил точку останова в своем коде непосредственно из командной строки GHCi, чтобы отслеживать значение n
, которое показало, что задержанное сопоставление с образцом не имело место.
Может ли проблема быть на самом деле с функцией enumFromTo
, или есть какая-то другая причина?