Объяснение о приоритете оператора Haskell и составе функций - PullRequest
3 голосов
/ 22 сентября 2019

Мне нужна помощь в понимании шаблона Haskell для задания Hackerrank «Репликация списка».Я не понимаю, в каком порядке выполняются функции.

f = concatMap . replicate

-- This part handles the Input and Output and can be used as it is. 
main :: IO ()
main = getContents >>=
       mapM_ print . (\(n:arr) -> f n arr) . map read . words

Функция getContents должна выдавать IO String подобно "2\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10".Я приблизительно понимаю, что происходит дальше, но я не понимаю, в каком порядке и с каким приоритетом.Я попытался выполнить words "2\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10 в ghci и передать результат в map read.Но тогда я получаю результат "[*** Exception: Prelude.read: no parse".Если я попытаюсь map read . words "2\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10", я получу результат "Couldn't match expected type ‘a -> [String]’ with actual type ‘[String]’".Как получается, что вся составная основная функция не выдает ошибок?

Я был бы очень признателен, если бы кто-нибудь помог мне понять всю основную функцию.В частности, какие функции выполняются, в каком порядке, с каким вводом и как я могу разрезать его на более мелкие (работающие!) Части, чтобы лучше понять его.

1 Ответ

4 голосов
/ 22 сентября 2019

Определяя по приглашению GHCi

g = (\(n:arr) -> f n arr)
 where
 f = concatMap . replicate

, мы получаем производный тип как g :: [Int] -> [Int].Это определяет тип read в map read (подача в g) как String -> Int, и все работает.Может быть проще следовать в более читаемой форме:

g :: [Int] -> [Int]
g (n:arr) = concatMap (replicate n) arr

Тип Int получен для n :: Int из-за replicate :: Int -> a -> [a], а тип для arr и (n:arr) :: [Int] следует из этого, потому что списки Хаскелла однородны, то есть имеют все элементы одного типа.

Самый общий тип read - read :: Read a => String -> a.Действительно, Int относится к классу Read.

Но что, если мы не знаем, является ли это Int, Double или чем-то еще?Тогда read не знает, какой формат анализировать, и поэтому мы получаем сообщение об ошибке "read: no parse".

Вот что происходит, когда мы пытаемся

(map read . words) "2\n3\n4\n"  =  map read $ words "2\n3\n4\n" 
                                =  map read (words "2\n3\n4\n")

$появляется там вместо ., когда мы открываем скобки.Из-за приоритета оператора ваша функция main на самом деле

main :: IO ()
main = getContents >>=
       (mapM_ print . (\(n:arr) -> f n arr) . map read . words)
     = getContents >>= (\s -> 
       (mapM_ print . g                     . map read . words) s)
     = getContents >>= (\s -> 
        mapM_ print $ g                     $ map read $ words  s)

Использование . вместо $ там, как вы это сделали, неправильно, потому что просто пропуск этих скобок неправильный, вызывает другое сообщение об ошибкеотносится к функциям, так как . ожидает функцию в качестве аргумента справа, но ["2","3","4"] не является функцией.

...