Как читать этот функциональный код

Возникли проблемы при чтении (интерпретации) этого функционального кода Miranda.

g = (foldr (+) 0) . (foldr ((:) . ((#) . (:[]))) [])

Я знаю что это делает

  • Рассчитайте размер списка, взяв длину через #
  • Создание списка из одного элемента, содержащего вышеуказанную длину исходного списка ввода
  • Свернуть новый список, используя foldr в одно целое число с операцией +0 на каждом элементе

Однако я смущен скобками и не вижу, куда вводится список ввода. Что делает самый правый [] конструктор делать?

Кроме того, почему этот код работает только через функцию g, но если я вызываю его напрямую, он выдает ошибку?

2 ответа

Решение

Короче, g это функция, которая возвращает длину списка

Давайте разберем функцию на несколько частей.

|| returns 1 for any input.
||   return_one "hoobar" = 1
return_one :: * -> num
return_one = (#) . (:[]) 

|| ignore first argument, insert 1 to the second argument.
||   insert_one "hoobar" [4,5,6] = [1,4,5,6]
insert_one :: *->[num]->[num]
insert_one = (:) . return_one

|| sum of list.
||   sum_list [7,8,9] = 24
sum_list :: [num] -> num 
sum_list = foldr (+) 0

|| generate list of 1 which as the same length of original list. 
||   change_one ["apple","banana"] = [1,1]
change_one :: [*] -> [num]
change_one = foldr insert_one []

|| return the length of the list.
||   g ["apple","banana"] = 2
g :: [*] -> num
g = sum_list . change_one

Я бы объяснил некоторые запутанные функции.

return_one

(:[]) это функция, которая создает список из одного элемента, и (#) возвращает длину. Строго говоря, (:[]) является (:) который занимает [] в качестве первого аргумента.

следовательно (:[]) "hoobar" = "hoobar":[] = ["hoobar"]и применяя (#) к нему возвращается 1.

change_one

Начинается с пустого списка [] и перебирает список со вставкой 1 вперед.

foldr insert_one [] ["apple","banana"]
= foldr insert_one [1] ["apple"]
= foldr insert_one [1,1] []

Я не знаю Миранду слишком хорошо, но на основе Хаскелла (я считаю, что различия между ними здесь будут незначительными, только с # будучи унарным оператором для длины списка, являясь единственным полусущественным и с || является синтаксисом комментария): . Состав функции:

(p . q) x = p (q x)
  || also can be written as:
p . q = \x -> p (q x)

Композиция функций является ассоциативной операцией, поэтому p . (q . r) знак равно (p . q) . r знак равно p . q . r,

Используя эту информацию, мы можем расширить ее с помощью определения .:

g      = (foldr (+) 0) . (foldr ((:) . ((#) . (:[]))) [])       || Original definition
g list = foldr (+) 0 (foldr ((:) . ((#) . (:[]))) [] list)
g list = foldr (+) 0 (foldr (\x -> (:) (((#) . (:[])) x)) [] list)
g list = foldr (+) 0 (foldr (\x -> (:) ((\y -> (#) ((:[]) y)) x)) [] list)

Это можно исправить еще немного:

g list = foldr (+) 0 (foldr (\x -> (:) ((\y -> (#)(y:[])) x)) [] list) || More conventional operator syntax for the innermost `:`
g list = foldr (+) 0 (foldr (\x -> (:) ((#)(x:[]))) [] list)           || Innermost lambda was applied to x. Substitute y for x.
g list = foldr (+) 0 (foldr (\x -> (:) ((#)([x]))) [] list)            || Apply innermost `:`
g list = foldr (+) 0 (foldr (\x -> (:) #[x])) [] list)                 || Remove unnecessary parentheses
g list = foldr (+) 0 (foldr (\x acc -> (:) (#[x]) acc) [] list)        || Explicitly write implicit argument. This particular step is called eta-expansion
g list = foldr (+) 0 (foldr (\x acc -> (:) 1 acc) [] list)             || #[x] is always 1, no matter what x is
g list = foldr (+) 0 (foldr (\x acc -> 1 : acc) [] list)               || More conventional syntax for `:`

Также обратите внимание, что foldr не применяется +0 к каждому элементу, как вы указали в вопросе. foldr op z (a:b:c:[]) становится op a (op b (op c z))) (a:b:c:[] это еще один способ написать [a,b,c]). Я всегда думал, что эта диаграмма полезна, чтобы понять, что:

диаграмма фальца

Кроме того, наиболее вероятной причиной возникновения ошибки при ее непосредственном вызове является то, что p . q x это не то же самое, что (p . q) x,

Другие вопросы по тегам