Haskell / Miranda: найдите тип функции

Вкратце: это прошлый экзаменационный вопрос на экзамене в Миранде, но синтаксис очень похож на Haskell.

Вопрос: Каков тип следующего выражения и что оно делает? (Определения функций length и swap приведены ниже).

(foldr (+) 0) . (foldr ((:) . length . (swap (:) [] )) [])

length [] = 0

length (x:xs) = 1 + length xs

swap f x y = f y x

Замечания:

Пожалуйста, не стесняйтесь отвечать в синтаксисе haskell - извините за использование звездочек в качестве политипов, но я не хотел переводить их неправильно в haskell. По сути, если одна переменная имеет тип *, а другая - *, это означает, что они могут быть любого типа, но оба они должны быть одного типа. Если у вас есть **, то это означает, что он может иметь тот же тип, что и *. Я думаю, что это соответствует a, b, c и т. Д. В использовании haskell.

Моя работа до сих пор

Из определения длины вы можете увидеть, что он находит длину списка чего-либо, так что это дает

length :: [*] -> num.

Из определения я думаю, что swap принимает функцию и два параметра и производит функцию с двумя переключенными параметрами, так что это дает

swap :: (* -> ** -> ***) -> ** -> [*] -> ***

Foldr принимает двоичную функцию (например, плюс) начальное значение и список и сворачивает список справа налево, используя эту функцию. Это дает

foldr :: (* -> ** -> **) -> ** -> [*] -> **)

Я знаю, что в композиции функций это право ассоциативно, поэтому, например, все, что находится справа от первой точки (.), Должно создать список, потому что оно будет передано в качестве аргумента для первого свёртывания.

Функция foldr выводит одно значение (результат свертывания списка), поэтому я знаю, что возвращаемый тип будет неким типом политипа, а не списком политипа.

Моя проблема

Я не уверен, куда идти отсюда на самом деле. Я вижу, что своп должен принимать другой аргумент, поэтому подразумевает ли это частичное приложение, что все это функция? Я совсем запутался!

2 ответа

Решение

Вы уже получили ответ, я просто запишу деривацию шаг за шагом, чтобы было легко увидеть все сразу:

xxf xs = foldr (+) 0 . foldr ((:) . length . flip (:) []) [] $ xs
       == sum $ foldr ((:) . length . (: [])) [] xs
       == sum $ foldr (\x -> (:) (length [x])) [] xs
       == sum $ foldr (\x r -> length [x]:r) [] xs
       == sum $ map   (\x -> length [x]) xs
       == sum [length [x] | x <- xs]  
       == sum [1 | x <- xs]
    -- == length xs
xxf :: (Num n) => [a] -> n

Так что в Миранде xxf xs = #xs, Я думаю, что его тип :: [*] -> num в синтаксисе Миранды.

Хаскеля length является :: [a] -> Int но, как определено здесь, это :: (Num n) => [a] -> n потому что он использует Num"s (+) и два литерала, 0 а также 1,

Если у вас возникли проблемы с визуализацией foldrэто просто

foldr (+) 0 (a:(b:(c:(d:(e:(...:(z:[])...))))))
          == a+(b+(c+(d+(e+(...+(z+ 0)...)))))
          == sum [a,b,c,d,e,...,z]

Давайте пройдемся по этому шаг за шагом.

length функция, очевидно, имеет тип, который вы описали; в Хаскеле это Num n => [a] -> n, Эквивалентная функция Хаскеля length (Оно использует Int вместо любого Num n).

swap Функция принимает функцию для вызова и отменяет свои первые два аргумента. Вы не совсем правильно поняли подпись; его (a -> b -> c) -> b -> a -> c, Эквивалентная функция Хаскеля flip,

foldr функция имеет тип, который вы описали; а именно (a -> b -> b) -> b -> [a] -> b, Эквивалентная функция Хаскеля foldr,

Теперь давайте посмотрим, что означает каждое подвыражение в основном выражении.

Выражение swap (:) [] принимает (:) функция и меняет свои аргументы. (:) функция имеет тип a -> [a] -> [a], так swap пинг это дает [a] -> a -> [a]; таким образом, все выражение имеет тип a -> [a] потому что поменялась функция применяется к [], В результате получается, что функция создает список из одного элемента, заданного этим элементом.

Для простоты давайте выделим эту часть в функцию:

singleton :: a -> [a]
singleton = swap (:) []

Теперь следующее выражение (:) . length . singleton, (:) функция все еще имеет тип a -> [a] -> [a]; что за (.) Функция делает то, что она составляет функции, так что если у вас есть функция foo :: a -> ... и функция bar :: b -> a, foo . bar будет иметь тип b -> ..., Выражение (:) . length таким образом имеет тип Num n => [a] -> [n] -> [n] (Помните, что length возвращает Num) и выражение (:) . length . singleton имеет тип Num => a -> [n] -> [n], То, что делает полученное выражение, довольно странно: при любом значении типа a и какой-то список, он будет игнорировать a и добавьте число 1 в этот список.

Для простоты давайте сделаем из этого функцию:

constPrependOne :: Num n => a -> [n] -> [n]
constPrependOne = (:) . length . singleton

Вы уже должны быть знакомы с foldr, Он выполняет правое сворачивание по списку с помощью функции. В этой ситуации это вызывает constPrependOne на каждом элементе, поэтому выражение foldr constPrependOne [] просто строит список единиц с равной длиной для списка ввода. Итак, давайте сделаем функцию из этого:

listOfOnesWithSameLength :: Num n => [a] -> [n]
listOfOnesWithSameLength = foldr constPrependOne []

Если у вас есть список [2, 4, 7, 2, 5], ты получишь [1, 1, 1, 1, 1] при подаче заявления listOfOnesWithSameLength,

Затем foldr (+) 0 функция является еще одной правой. Это эквивалентно sum функция в Хаскеле; суммирует элементы списка.

Итак, давайте сделаем функцию:

sum :: Num n => [n] -> n
sum = foldr (+) 0

Если вы сейчас составляете функции:

func = sum . listOfOnesWithSameLength

... вы получите результирующее выражение. Учитывая некоторый список, он создает список равной длины, состоящий только из одних, а затем суммирует элементы этого списка. Другими словами, ведет себя так же, как length, только используя гораздо более медленный алгоритм. Итак, последняя функция:

inefficientLength :: Num n => [a] -> n
inefficientLength = sum . listOfOnesWithSameLength
Другие вопросы по тегам