it-swarm.com.ru

Передача функции в качестве параметра и возвращение функции - Haskell

У меня есть график функции f(n), где он возвращает 

5  if n = 0 
2  if n = 1 
-4 if n = 2 
1  if n = 3 
9  if n = 4 
8  if n = 5 
9  if n = 6 
0  otherwise 

и я хотел написать функцию, которая будет представлять граф с одним списком с парами:

type Nat0 = Int 
type Z = Int
type List = [Z] 
type Graph = [(Nat0,Z)] 

list_to_graph :: List -> Graph
list_to_graph x = list_to_graph' ([0..(length x)-1 ]) (x)

list_to_graph' :: [Int] -> List -> Graph
list_to_graph' (x:xs) (y:ys) = [(x, y)] ++ list_to_graph' (xs) (ys)
list_to_graph' [] [] = []

И вот что я сделал здесь. Передача списка [5,2,-4,1,9,8,9] возвращается 

*Main> list_to_graph [5,2,-4,1,9,8,9]
[(0,5),(1,2),(2,-4),(3,1),(4,9),(5,8),(6,9)]

и вот функция, которая делает oposite:

graph_to_list :: Graph -> List
graph_to_list (x:xs) = [snd (x)] ++ graph_to_list(xs)
graph_to_list []= [] 

где прохождение графа [(0,5),(1,2),(2,-4),(3,1),(4,9),(5,8),(6,9)]

*Main> graph_to_list [(0,5),(1,2),(2,-4),(3,1),(4,9),(5,8),(6,9)]
[5,2,-4,1,9,8,9]

Вопрос:

Я не понимаю, как написать что-то вроде этого: 

type Function = (Nat0 -> Z) 

function_to_list :: Function -> List

или же 

list_to_function :: List -> Function

или то же самое для графа 

function_to_graph :: Function -> Graph
graph_to_function :: Graph -> Function

Я прочитал функции высшего порядка по этой ссылке, но я не могу понять, как это на самом деле работает.

Я полагаю, что в function_to_list мне нужно будет передать функцию с этой нотацией (Nat0 -> Z) (которая на самом деле Int -> Int), и она должна вернуть List с [Z] (которая [Int]). Но как мне это сделать? Это похоже на передачу той же самой функции себе? 

Еще более запутанным является list_to_function, каким должен быть результат?

Если кто-то может объяснить мне функции высшего порядка на некоторых моих примерах, я был бы очень благодарен!

Правка:

Чтобы быть более понятным, вот чего я хочу достичь: 

(list_to_graph . graph_to_list) = λ x. x
(graph_to_list . list_to_graph) = λ x. x

Как я показал выше, если я передам список list_to_graph, он вернет график, а graph_to_list сделает невозможным

(list_to_function . function_to_list) = λ x. x
(function_to_list . list_to_function) = λ x. x

это то же самое, что я хочу сделать с двумя другими функциями. Если я применяю function_to_list к list_to_function, так как function_to_list возвращает List и list_to_function принимает List, он должен вернуть функцию, которая вычеркнет элементы из списка и применит ее к Function, которая вернет Z

Что я уже понял: 

function_to_list :: Function-> List
function_to_list f = [f(x) | x <- [0..6]]

function :: Function
function n
         | n == 0 = 5
         | n == 1 = 2 
         | n == 2 = (-4)
         | n == 3 = 1
         | n == 4 = 9
         | n == 5 = 8
         | n == 6 = 9
         | otherwise = 0

Как ответ ниже предложил.

*Main> function_to_list function 
[5,2,-4,1,9,8,9]

Что я хочу сделать, это сделать этот function :: Function в моем list_to_function

7
cheshire

Итак, вы пытаетесь получить 

list_to_function :: List -> Function

правильный? Давайте вместо этого сделаем Graph, это станет ближе к сути. Давайте начнем писать.

graph_to_function gph = _ -- something of type Function

Поэтому мы хотим создать что-то типа Function, то есть Nat0 -> Z. Самый скучный способ сделать это с помощью лямбды:

graph_to_function gph = \n -> _ -- something of type Z

Отлично, теперь у нас есть Graph с именем gph и Nat0 с именем n, и мы хотим создать Z. Здесь мы можем просто найти в списке n. Есть несколько способов сделать это, вот один:

graph_to_function gph = \n -> head ([ y | (x,y) <- gph, x == n ] ++ [0])

Я ставлю ++ [0] в конце на тот случай, если понимание списка окажется пустым, то есть мы не нашли n в области графа. Готово!

Интересный факт, что функции в Haskell по умолчанию карри, поэтому

f x y z = ...
f = \x -> \y -> \z -> ...

эквивалентны. То есть graph_to_function - это то же самое, что и функция с двумя аргументами. Таким образом, мы можем переместить n в левую часть определения:

graph_to_function :: Graph -> Function
graph_to_function gph n = head ([ y | (x,y) <- gph, x == n ] ++ [0])

Это выглядит немного странно, когда в сигнатуре есть только один аргумент - это два аргумента в уравнении, но это то, что вы видите в дикой природе, когда привыкнете к нему.

Надеюсь это поможет!

2
luqui
list_to_graph' :: [Int] -> List -> Graph
list_to_graph' (x:xs) (y:ys) = [(x, y)] ++ list_to_graph' (xs) (ys)
list_to_graph' [] [] = []

Эта функция существует и называется Zip

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

graph_to_list :: Graph -> List
graph_to_list (x:xs) = [snd (x)] ++ graph_to_list(xs)
graph_to_list []= []

Вы можете написать эту функцию как, 

graph_to_list = map snd

или же 

graph_to_list xs = [snd x | x <- xs]

или же 

graph_to_list xs = [a | (a,b) <- xs]

В соответствии с этим, 

Я не понимаю, как написать что-то вроде этого:

type Function = (Nat0 -> Z) 

function_to_list :: Function -> List

Если я вас правильно понимаю, вы хотели бы иметь возможность создать «образ f», то есть список всех значений f x для всех x в домене f. То, что в теории может выглядеть, 

[f(x) | x <- DOMAIN f]

Однако в общем случае нет способа узнать область заданной функции (а тем более путь ее обхода). То же самое касается перевода функции в граф. Чтобы реализовать такие «преобразования», вы должны предоставить в качестве аргумента как вашу функцию f :: A -> B, так и список xs :: A с точками своего домена, которые вы хотите рассмотреть. 

3
Jorge Adriano